crossplane
301 строка · 7.7 Кб
1/*
2Copyright 2023 The Crossplane Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package upbound18
19import (20"fmt"21"net/url"22"path/filepath"23"testing"24
25"github.com/alecthomas/kong"26"github.com/google/go-cmp/cmp"27"github.com/google/go-cmp/cmp/cmpopts"28"github.com/spf13/afero"29
30"github.com/crossplane/crossplane-runtime/pkg/errors"31"github.com/crossplane/crossplane-runtime/pkg/test"32
33"github.com/crossplane/crossplane/internal/xpkg/upbound/config"34)
35
36var (37defaultConfigJSON = `{38"upbound": {
39"default": "default",
40"profiles": {
41"default": {
42"id": "someone@upbound.io",
43"type": "user",
44"session": "a token"
45}
46}
47}
48}
49`
50baseConfigJSON = `{51"upbound": {
52"default": "default",
53"profiles": {
54"default": {
55"id": "someone@upbound.io",
56"type": "user",
57"session": "a token",
58"base": {
59"UP_DOMAIN": "https://local.upbound.io",
60"UP_ACCOUNT": "my-org",
61"UP_INSECURE_SKIP_TLS_VERIFY": "true"
62}
63},
64"cool-profile": {
65"id": "someone@upbound.io",
66"type": "user",
67"session": "a token",
68"base": {
69"UP_DOMAIN": "https://local.upbound.io",
70"UP_ACCOUNT": "my-org",
71"UP_INSECURE_SKIP_TLS_VERIFY": "true"
72}
73}
74}
75}
76}
77`
78)
79
80func withConfig(config string) Option {81return func(ctx *Context) {82// establish fs and create config.json83fs := afero.NewMemMapFs()84fs.MkdirAll(filepath.Dir("/.up/"), 0755)85f, _ := fs.Create("/.up/config.json")86
87f.WriteString(config)88
89ctx.fs = fs90}91}
92
93func withFS(fs afero.Fs) Option {94return func(ctx *Context) {95ctx.fs = fs96}97}
98
99func withPath(p string) Option {100return func(ctx *Context) {101ctx.cfgPath = p102}103}
104
105func withURL(uri string) *url.URL {106u, _ := url.Parse(uri)107return u108}
109
110func TestNewFromFlags(t *testing.T) {111type args struct {112flags []string113opts []Option114}115type want struct {116err error117c *Context118}119
120cases := map[string]struct {121reason string122args args
123want want
124}{125"NoPreExistingProfile": {126reason: "We should successfully return a Context if a pre-existing profile does not exist.",127args: args{128flags: []string{},129opts: []Option{130withFS(afero.NewMemMapFs()),131},132},133want: want{134c: &Context{135Account: "",136APIEndpoint: withURL("https://api.upbound.io"),137Cfg: &config.Config{},138Domain: withURL("https://upbound.io"),139Profile: config.Profile{},140RegistryEndpoint: withURL("https://xpkg.upbound.io"),141},142},143},144"ErrorSuppliedNotExist": {145reason: "We should return an error if profile is supplied and it does not exist.",146args: args{147flags: []string{148"--profile=not-here",149},150},151want: want{152err: errors.Errorf(errProfileNotFoundFmt, "not-here"),153},154},155"SuppliedNotExistAllowEmpty": {156reason: "We should successfully return a Context if a supplied profile does not exist and.",157args: args{158flags: []string{159"--profile=not-here",160},161opts: []Option{162withFS(afero.NewMemMapFs()),163AllowMissingProfile(),164},165},166want: want{167c: &Context{168ProfileName: "not-here",169Account: "",170APIEndpoint: withURL("https://api.upbound.io"),171Cfg: &config.Config{},172Domain: withURL("https://upbound.io"),173Profile: config.Profile{},174RegistryEndpoint: withURL("https://xpkg.upbound.io"),175},176},177},178"PreExistingProfileNoBaseConfig": {179reason: "We should successfully return a Context if a pre-existing profile exists, but does not have a base config",180args: args{181flags: []string{},182opts: []Option{183withConfig(defaultConfigJSON),184withPath("/.up/config.json"),185},186},187want: want{188c: &Context{189ProfileName: "default",190Account: "",191APIEndpoint: withURL("https://api.upbound.io"),192Domain: withURL("https://upbound.io"),193InsecureSkipTLSVerify: false,194Profile: config.Profile{195ID: "someone@upbound.io",196Type: config.UserProfileType,197Session: "a token",198Account: "",199},200RegistryEndpoint: withURL("https://xpkg.upbound.io"),201Token: "",202},203},204},205"PreExistingProfileBaseConfigSetProfile": {206reason: "We should return a Context that includes the persisted Profile from base config",207args: args{208flags: []string{},209opts: []Option{210withConfig(baseConfigJSON),211withPath("/.up/config.json"),212},213},214want: want{215c: &Context{216ProfileName: "default",217Account: "my-org",218APIEndpoint: withURL("https://api.local.upbound.io"),219Domain: withURL("https://local.upbound.io"),220InsecureSkipTLSVerify: true,221Profile: config.Profile{222ID: "someone@upbound.io",223Type: config.UserProfileType,224Session: "a token",225Account: "",226BaseConfig: map[string]string{227"UP_ACCOUNT": "my-org",228"UP_DOMAIN": "https://local.upbound.io",229"UP_INSECURE_SKIP_TLS_VERIFY": "true",230},231},232RegistryEndpoint: withURL("https://xpkg.local.upbound.io"),233Token: "",234},235},236},237"PreExistingBaseConfigOverrideThroughFlags": {238reason: "We should return a Context that includes the persisted Profile from base config overridden based on flags",239args: args{240flags: []string{241"--profile=cool-profile",242"--account=not-my-org",243fmt.Sprintf("--domain=%s", withURL("http://a.domain.org")),244fmt.Sprintf("--override-api-endpoint=%s", withURL("http://not.a.url")),245},246opts: []Option{247withConfig(baseConfigJSON),248withPath("/.up/config.json"),249},250},251want: want{252c: &Context{253ProfileName: "cool-profile",254Account: "not-my-org",255APIEndpoint: withURL("http://not.a.url"),256Domain: withURL("http://a.domain.org"),257InsecureSkipTLSVerify: true,258Profile: config.Profile{259ID: "someone@upbound.io",260Type: config.UserProfileType,261Session: "a token",262Account: "",263BaseConfig: map[string]string{264"UP_ACCOUNT": "my-org",265"UP_DOMAIN": "https://local.upbound.io",266"UP_INSECURE_SKIP_TLS_VERIFY": "true",267},268},269RegistryEndpoint: withURL("http://xpkg.a.domain.org"),270Token: "",271},272},273},274}275
276for name, tc := range cases {277t.Run(name, func(t *testing.T) {278flags := Flags{}279parser, _ := kong.New(&flags)280parser.Parse(tc.args.flags)281
282c, err := NewFromFlags(flags, tc.args.opts...)283
284if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {285t.Errorf("\n%s\nNewFromFlags(...): -want error, +got error:\n%s", tc.reason, diff)286}287
288if diff := cmp.Diff(tc.want.c, c,289cmpopts.IgnoreUnexported(Context{}),290// NOTE(tnthornton): we're not concerned about the FSSource's291// internal components.292cmpopts.IgnoreFields(Context{}, "CfgSrc"),293// NOTE(tnthornton) we're not concerned about the Cfg's294// internal components.295cmpopts.IgnoreFields(Context{}, "Cfg"),296); diff != "" {297t.Errorf("\n%s\nNewFromFlags(...): -want error, +got error:\n%s", tc.reason, diff)298}299})300}301}
302