Remove clusters, users, reconcilation and config import

All of these entities are no longer required and should be deleted.

Change-Id: Id4776efe668265df6961a38ce984b8ac37b27097
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Relates-To: #327
This commit is contained in:
Ruslan Aliev 2020-08-30 19:11:48 -05:00
parent d87eea6544
commit 5d52339bd6
47 changed files with 111 additions and 2199 deletions

View File

@ -82,8 +82,6 @@ func TestNewClusterStatusCmd(t *testing.T) {
func clusterStatusTestSettings() config.Factory { func clusterStatusTestSettings() config.Factory {
return func() (*config.Config, error) { return func() (*config.Config, error) {
return &config.Config{ return &config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": nil},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": nil},
Contexts: map[string]*config.Context{ Contexts: map[string]*config.Context{
"testContext": {Manifest: "testManifest"}, "testContext": {Manifest: "testManifest"},
}, },

View File

@ -34,7 +34,6 @@ func NewConfigCommand(cfgFactory config.Factory) *cobra.Command {
configRootCmd.AddCommand(NewGetManagementConfigCommand(cfgFactory)) configRootCmd.AddCommand(NewGetManagementConfigCommand(cfgFactory))
configRootCmd.AddCommand(NewSetManagementConfigCommand(cfgFactory)) configRootCmd.AddCommand(NewSetManagementConfigCommand(cfgFactory))
configRootCmd.AddCommand(NewImportCommand(cfgFactory))
configRootCmd.AddCommand(NewUseContextCommand(cfgFactory)) configRootCmd.AddCommand(NewUseContextCommand(cfgFactory))
configRootCmd.AddCommand(NewGetManifestCommand(cfgFactory)) configRootCmd.AddCommand(NewGetManifestCommand(cfgFactory))

View File

@ -20,8 +20,6 @@ import (
"fmt" "fmt"
"testing" "testing"
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
cmd "opendev.org/airship/airshipctl/cmd/config" cmd "opendev.org/airship/airshipctl/cmd/config"
"opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/testutil" "opendev.org/airship/airshipctl/testutil"
@ -94,17 +92,10 @@ func TestNoContextsGetContextCmd(t *testing.T) {
} }
func getNamedTestContext(contextName string) *config.Context { func getNamedTestContext(contextName string) *config.Context {
kContext := &kubeconfig.Context{
Namespace: "dummy_namespace",
AuthInfo: "dummy_user",
Cluster: fmt.Sprintf("dummycluster_%s", config.Ephemeral),
}
newContext := &config.Context{ newContext := &config.Context{
NameInKubeconf: fmt.Sprintf("%s_%s", contextName, config.Ephemeral), NameInKubeconf: fmt.Sprintf("%s_%s", contextName, config.Ephemeral),
Manifest: fmt.Sprintf("Manifest_%s", contextName), Manifest: fmt.Sprintf("Manifest_%s", contextName),
} }
newContext.SetKubeContext(kContext)
return newContext return newContext
} }

View File

@ -1,62 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/config"
)
const (
useImportLong = `
Merge the clusters, contexts, and users from an existing kubeConfig file into the airshipctl config file.
`
useImportExample = `
# Import from a kubeConfig file"
airshipctl config import $HOME/.kube/config
`
)
// NewImportCommand creates a command that merges clusters, contexts, and
// users from a kubeConfig file into the airshipctl config file.
func NewImportCommand(cfgFactory config.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "import <kubeConfig>",
Short: "Merge information from a kubernetes config file",
Long: useImportLong[1:],
Example: useImportExample,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := cfgFactory()
if err != nil {
return err
}
kubeConfigPath := args[0]
err = cfg.ImportFromKubeConfig(kubeConfigPath)
if err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "Updated airship config with content imported from %q.\n", kubeConfigPath)
return nil
},
}
return cmd
}

View File

@ -1,55 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config_test
import (
"errors"
"testing"
cmd "opendev.org/airship/airshipctl/cmd/config"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/testutil"
)
func TestConfigImport(t *testing.T) {
settings := func() (*config.Config, error) {
return testutil.DummyConfig(), nil
}
cmdTests := []*testutil.CmdTest{
{
Name: "config-import-with-help",
CmdLine: "--help",
Cmd: cmd.NewImportCommand(nil),
},
{
Name: "config-import-no-args",
CmdLine: "",
Cmd: cmd.NewImportCommand(settings),
Error: errors.New("accepts 1 arg(s), received 0"),
},
{
Name: "config-import-file-does-not-exist",
CmdLine: "foo",
Cmd: cmd.NewImportCommand(settings),
Error: errors.New("stat foo: no such file or directory"),
},
}
for _, tt := range cmdTests {
testutil.RunTest(t, tt)
}
}

View File

@ -32,10 +32,7 @@ Create or modify a context in the airshipctl config files.
setContextExample = ` setContextExample = `
# Create a new context named "exampleContext" # Create a new context named "exampleContext"
airshipctl config set-context exampleContext \ airshipctl config set-context exampleContext \
--namespace=kube-system \
--manifest=exampleManifest \ --manifest=exampleManifest \
--user=exampleUser
--cluster-type=target
--encryption-config=exampleEncryptionConfig --encryption-config=exampleEncryptionConfig
# Update the manifest of the current-context # Update the manifest of the current-context
@ -91,18 +88,6 @@ func NewSetContextCommand(cfgFactory config.Factory) *cobra.Command {
func addSetContextFlags(o *config.ContextOptions, cmd *cobra.Command) { func addSetContextFlags(o *config.ContextOptions, cmd *cobra.Command) {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(
&o.Cluster,
"cluster",
"",
"set the cluster for the specified context")
flags.StringVar(
&o.AuthInfo,
"user",
"",
"set the user for the specified context")
flags.StringVar( flags.StringVar(
&o.Manifest, &o.Manifest,
"manifest", "manifest",
@ -115,12 +100,6 @@ func addSetContextFlags(o *config.ContextOptions, cmd *cobra.Command) {
"", "",
"set the encryption config for the specified context") "set the encryption config for the specified context")
flags.StringVar(
&o.Namespace,
"namespace",
"",
"set the namespace for the specified context")
flags.StringVar( flags.StringVar(
&o.ClusterType, &o.ClusterType,
"cluster-type", "cluster-type",

View File

@ -30,11 +30,9 @@ import (
) )
const ( const (
testUser = "admin@kubernetes"
defaultManifest = "edge_cloud"
defaultNamespace = "kube-system"
testManifest = "test_manifest"
testEncryptionConfig = "test_encryption_config" testEncryptionConfig = "test_encryption_config"
defaultManifest = "edge_cloud"
testManifest = "test_manifest"
) )
type setContextTest struct { type setContextTest struct {
@ -86,9 +84,7 @@ func TestSetContext(t *testing.T) {
contextName: "dummycontext", contextName: "dummycontext",
flags: []string{ flags: []string{
"--cluster-type=target", "--cluster-type=target",
"--user=" + testUser,
"--manifest=" + defaultManifest, "--manifest=" + defaultManifest,
"--namespace=" + defaultNamespace,
"--encryption-config=" + testEncryptionConfig, "--encryption-config=" + testEncryptionConfig,
}, },
givenConfig: given, givenConfig: given,
@ -153,9 +149,6 @@ func (test setContextTest) run(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, afterRunContext) require.NotNil(t, afterRunContext)
afterKcontext := afterRunContext.KubeContext()
require.NotNil(t, afterKcontext)
if test.manifest != "" { if test.manifest != "" {
assert.EqualValues(t, afterRunContext.Manifest, test.manifest) assert.EqualValues(t, afterRunContext.Manifest, test.manifest)
} }

View File

@ -9,7 +9,6 @@ Available Commands:
get-management-config View a management config or all management configs defined in the airshipctl config get-management-config View a management config or all management configs defined in the airshipctl config
get-manifest Get a manifest information from the airshipctl config get-manifest Get a manifest information from the airshipctl config
help Help about any command help Help about any command
import Merge information from a kubernetes config file
init Generate initial configuration files for airshipctl init Generate initial configuration files for airshipctl
set-context Manage contexts set-context Manage contexts
set-encryption-config Manage encryption configs in airship config set-encryption-config Manage encryption configs in airship config

View File

@ -1,13 +0,0 @@
Error: stat foo: no such file or directory
Usage:
import <kubeConfig> [flags]
Examples:
# Import from a kubeConfig file"
airshipctl config import $HOME/.kube/config
Flags:
-h, --help help for import

View File

@ -1,13 +0,0 @@
Error: accepts 1 arg(s), received 0
Usage:
import <kubeConfig> [flags]
Examples:
# Import from a kubeConfig file"
airshipctl config import $HOME/.kube/config
Flags:
-h, --help help for import

View File

@ -1,13 +0,0 @@
Merge the clusters, contexts, and users from an existing kubeConfig file into the airshipctl config file.
Usage:
import <kubeConfig> [flags]
Examples:
# Import from a kubeConfig file"
airshipctl config import $HOME/.kube/config
Flags:
-h, --help help for import

View File

@ -6,10 +6,7 @@ Examples:
# Create a new context named "exampleContext" # Create a new context named "exampleContext"
airshipctl config set-context exampleContext \ airshipctl config set-context exampleContext \
--namespace=kube-system \
--manifest=exampleManifest \ --manifest=exampleManifest \
--user=exampleUser
--cluster-type=target
--encryption-config=exampleEncryptionConfig --encryption-config=exampleEncryptionConfig
# Update the manifest of the current-context # Update the manifest of the current-context
@ -19,12 +16,9 @@ airshipctl config set-context \
Flags: Flags:
--cluster string set the cluster for the specified context
--cluster-type string set the cluster-type for the specified context --cluster-type string set the cluster-type for the specified context
--current update the current context --current update the current context
--encryption-config string set the encryption config for the specified context --encryption-config string set the encryption config for the specified context
-h, --help help for set-context -h, --help help for set-context
--manifest string set the manifest for the specified context --manifest string set the manifest for the specified context
--namespace string set the namespace for the specified context
--user string set the user for the specified context

View File

@ -7,10 +7,7 @@ Examples:
# Create a new context named "exampleContext" # Create a new context named "exampleContext"
airshipctl config set-context exampleContext \ airshipctl config set-context exampleContext \
--namespace=kube-system \
--manifest=exampleManifest \ --manifest=exampleManifest \
--user=exampleUser
--cluster-type=target
--encryption-config=exampleEncryptionConfig --encryption-config=exampleEncryptionConfig
# Update the manifest of the current-context # Update the manifest of the current-context
@ -20,11 +17,8 @@ airshipctl config set-context \
Flags: Flags:
--cluster string set the cluster for the specified context
--cluster-type string set the cluster-type for the specified context --cluster-type string set the cluster-type for the specified context
--current update the current context --current update the current context
--encryption-config string set the encryption config for the specified context --encryption-config string set the encryption config for the specified context
-h, --help help for set-context -h, --help help for set-context
--manifest string set the manifest for the specified context --manifest string set the manifest for the specified context
--namespace string set the namespace for the specified context
--user string set the user for the specified context

View File

@ -1,30 +1,18 @@
Context: ContextBar Context: ContextBar
contextKubeconf: ContextBar_ephemeral contextKubeconf: ContextBar_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextBar manifest: Manifest_ContextBar
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user
Context: ContextBaz Context: ContextBaz
contextKubeconf: ContextBaz_ephemeral contextKubeconf: ContextBaz_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextBaz manifest: Manifest_ContextBaz
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user
Context: ContextFoo Context: ContextFoo
contextKubeconf: ContextFoo_ephemeral contextKubeconf: ContextFoo_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextFoo manifest: Manifest_ContextFoo
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user

View File

@ -1,10 +1,6 @@
Context: ContextFoo Context: ContextFoo
contextKubeconf: ContextFoo_ephemeral contextKubeconf: ContextFoo_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextFoo manifest: Manifest_ContextFoo
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user

View File

@ -1,10 +1,6 @@
Context: ContextBaz Context: ContextBaz
contextKubeconf: ContextBaz_ephemeral contextKubeconf: ContextBaz_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextBaz manifest: Manifest_ContextBaz
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user

View File

@ -1,30 +1,18 @@
Context: ContextBar Context: ContextBar
contextKubeconf: ContextBar_ephemeral contextKubeconf: ContextBar_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextBar manifest: Manifest_ContextBar
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user
Context: ContextBaz Context: ContextBaz
contextKubeconf: ContextBaz_ephemeral contextKubeconf: ContextBaz_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextBaz manifest: Manifest_ContextBaz
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user
Context: ContextFoo Context: ContextFoo
contextKubeconf: ContextFoo_ephemeral contextKubeconf: ContextFoo_ephemeral
managementConfiguration: ""
manifest: Manifest_ContextFoo manifest: Manifest_ContextFoo
LocationOfOrigin: ""
cluster: dummycluster_ephemeral
namespace: dummy_namespace
user: dummy_user

View File

@ -27,7 +27,6 @@ Manage the airshipctl config file
* [airshipctl config get-encryption-config](airshipctl_config_get-encryption-config.md) - Get an encryption config information from the airshipctl config * [airshipctl config get-encryption-config](airshipctl_config_get-encryption-config.md) - Get an encryption config information from the airshipctl config
* [airshipctl config get-management-config](airshipctl_config_get-management-config.md) - View a management config or all management configs defined in the airshipctl config * [airshipctl config get-management-config](airshipctl_config_get-management-config.md) - View a management config or all management configs defined in the airshipctl config
* [airshipctl config get-manifest](airshipctl_config_get-manifest.md) - Get a manifest information from the airshipctl config * [airshipctl config get-manifest](airshipctl_config_get-manifest.md) - Get a manifest information from the airshipctl config
* [airshipctl config import](airshipctl_config_import.md) - Merge information from a kubernetes config file
* [airshipctl config init](airshipctl_config_init.md) - Generate initial configuration files for airshipctl * [airshipctl config init](airshipctl_config_init.md) - Generate initial configuration files for airshipctl
* [airshipctl config set-context](airshipctl_config_set-context.md) - Manage contexts * [airshipctl config set-context](airshipctl_config_set-context.md) - Manage contexts
* [airshipctl config set-encryption-config](airshipctl_config_set-encryption-config.md) - Manage encryption configs in airship config * [airshipctl config set-encryption-config](airshipctl_config_set-encryption-config.md) - Manage encryption configs in airship config

View File

@ -17,10 +17,7 @@ airshipctl config set-context NAME [flags]
# Create a new context named "exampleContext" # Create a new context named "exampleContext"
airshipctl config set-context exampleContext \ airshipctl config set-context exampleContext \
--namespace=kube-system \
--manifest=exampleManifest \ --manifest=exampleManifest \
--user=exampleUser
--cluster-type=target
--encryption-config=exampleEncryptionConfig --encryption-config=exampleEncryptionConfig
# Update the manifest of the current-context # Update the manifest of the current-context
@ -33,14 +30,11 @@ airshipctl config set-context \
### Options ### Options
``` ```
--cluster string set the cluster for the specified context
--cluster-type string set the cluster-type for the specified context --cluster-type string set the cluster-type for the specified context
--current update the current context --current update the current context
--encryption-config string set the encryption config for the specified context --encryption-config string set the encryption config for the specified context
-h, --help help for set-context -h, --help help for set-context
--manifest string set the manifest for the specified context --manifest string set the manifest for the specified context
--namespace string set the namespace for the specified context
--user string set the user for the specified context
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@ -66,6 +66,7 @@ func NewCommand(cfgFactory config.Factory) (*Command, error) {
// Init runs clusterctl init // Init runs clusterctl init
func (c *Command) Init() error { func (c *Command) Init() error {
log.Printf("config %s \n context %s", c.kubeconfigPath, c.kubeconfigContext)
return c.client.Init(c.kubeconfigPath, c.kubeconfigContext) return c.client.Init(c.kubeconfigPath, c.kubeconfigContext)
} }

View File

@ -1,25 +1,7 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
container:
volume: /tmp/airship:/config
image: quay.io/airshipit/isogen:latest-ubuntu_focal
containerRuntime: docker
builder:
userDataFileName: user-data
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
remoteDirect:
isoUrl: http://localhost:8099/ubuntu-focal.iso
clusters:
dummycluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummycluster_ephemeral
contexts: contexts:
dummy_cluster: dummy_cluster:
contextKubeconf: dummy_cluster contextKubeconf: dummycluster_ephemeral
manifest: dummy_manifest manifest: dummy_manifest
currentContext: dummy_cluster currentContext: dummy_cluster
kind: Config kind: Config
@ -39,5 +21,3 @@ manifests:
url: http://dummy.url.com/primary.git url: http://dummy.url.com/primary.git
subPath: site subPath: site
targetPath: testdata targetPath: testdata
users:
dummy_user: {}

View File

@ -1,48 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/yaml"
)
// AuthInfo contains kubeConfig AuthInfo Object
type AuthInfo struct {
// KubeConfig AuthInfo Object
authInfo *api.AuthInfo
}
// AuthInfo functions
func (c *AuthInfo) String() string {
kauthinfo := c.KubeAuthInfo()
kyaml, err := yaml.Marshal(&kauthinfo)
if err != nil {
return ""
}
return string(kyaml)
}
// KubeAuthInfo returns kubeConfig AuthInfo Object
func (c *AuthInfo) KubeAuthInfo() *api.AuthInfo {
return c.authInfo
}
// SetKubeAuthInfo sets kubeConfig in AuthInfo
func (c *AuthInfo) SetKubeAuthInfo(kc *api.AuthInfo) {
c.authInfo = kc
}

View File

@ -1,82 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/testutil"
)
func TestGetAuthInfos(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
authinfos, err := conf.GetAuthInfos()
require.NoError(t, err)
assert.Len(t, authinfos, 3)
}
func TestGetAuthInfo(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
authinfo, err := conf.GetAuthInfo("def-user")
require.NoError(t, err)
// Test Positives
assert.EqualValues(t, authinfo.KubeAuthInfo().Username, "dummy_username")
// Test Wrong Cluster
_, err = conf.GetAuthInfo("unknown")
assert.Error(t, err)
}
func TestAddAuthInfo(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
co := testutil.DummyAuthInfoOptions()
authinfo := conf.AddAuthInfo(co)
assert.EqualValues(t, conf.AuthInfos[co.Name], authinfo)
}
func TestModifyAuthInfo(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
co := testutil.DummyAuthInfoOptions()
authinfo := conf.AddAuthInfo(co)
co.Username += stringDelta
co.Password = newPassword
co.ClientCertificate = newCertificate
co.ClientKey = newKey
co.Token = newToken
conf.ModifyAuthInfo(authinfo, co)
modifiedAuthinfo, err := conf.GetAuthInfo(co.Name)
assert.NoError(t, err)
assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Username, co.Username)
assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Password, co.Password)
assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().ClientCertificate, co.ClientCertificate)
assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().ClientKey, co.ClientKey)
assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Token, co.Token)
assert.EqualValues(t, modifiedAuthinfo, authinfo)
}

View File

@ -1,141 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"strings"
"k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/yaml"
)
// Cluster contains information about how to communicate with a kubernetes cluster
type Cluster struct {
// Complex cluster name defined by the using <cluster name>_<cluster type>)
NameInKubeconf string `json:"clusterKubeconf"`
// KubeConfig Cluster Object
cluster *api.Cluster
// Management configuration which will be used for all hosts in the cluster
ManagementConfiguration string `json:"managementConfiguration"`
}
// ClusterPurpose encapsulates the Cluster Type as an enumeration
type ClusterPurpose struct {
// Cluster map of referenceable names to cluster configs
ClusterTypes map[string]*Cluster `json:"clusterType"`
}
// ClusterComplexName holds the complex cluster name information
// Encapsulates the different operations around using it.
type ClusterComplexName struct {
Name string
Type string
}
func (c *Cluster) String() string {
cyaml, err := yaml.Marshal(&c)
if err != nil {
return ""
}
kcluster := c.KubeCluster()
kyaml, err := yaml.Marshal(&kcluster)
if err != nil {
return string(cyaml)
}
return fmt.Sprintf("%s\n%s", string(cyaml), string(kyaml))
}
// PrettyString returns cluster information in a formatted string
func (c *Cluster) PrettyString() string {
clusterName := NewClusterComplexNameFromKubeClusterName(c.NameInKubeconf)
return fmt.Sprintf("Cluster: %s\n%s:\n%s", clusterName.Name, clusterName.Type, c)
}
// KubeCluster returns KubeConfig Cluster Object
func (c *Cluster) KubeCluster() *api.Cluster {
return c.cluster
}
// SetKubeCluster sets cluster in KubeConfig
func (c *Cluster) SetKubeCluster(kc *api.Cluster) {
c.cluster = kc
}
// String returns cluster's complex name, formed by combining name and type with a delimiter('_')
func (c *ClusterComplexName) String() string {
return strings.Join([]string{c.Name, c.Type}, AirshipClusterNameSeparator)
}
// ValidClusterType checks for the possible options for cluster type
// Returns error when invalid cluster type is given
func ValidClusterType(clusterType string) error {
for _, validType := range AllClusterTypes {
if clusterType == validType {
return nil
}
}
return fmt.Errorf("cluster type must be one of %v", AllClusterTypes)
}
// NewClusterPurpose is a convenience function that returns a new ClusterPurpose
func NewClusterPurpose() *ClusterPurpose {
return &ClusterPurpose{
ClusterTypes: make(map[string]*Cluster),
}
}
// NewClusterComplexName returns a ClusterComplexName with the given name and type.
func NewClusterComplexName(clusterName, clusterType string) ClusterComplexName {
return ClusterComplexName{
Name: clusterName,
Type: clusterType,
}
}
// NewClusterComplexNameFromKubeClusterName takes the name of a cluster in a
// format which might be found in a kubeconfig file. This may be a simple
// string (e.g. myCluster), or it may be prepended with the type of the cluster
// (e.g. myCluster_target)
//
// If a valid cluster type was appended, the returned ClusterComplexName will
// have that type. If no cluster type is provided, the
// AirshipDefaultClusterType will be used.
func NewClusterComplexNameFromKubeClusterName(kubeClusterName string) ClusterComplexName {
parts := strings.Split(kubeClusterName, AirshipClusterNameSeparator)
if len(parts) == 1 {
return NewClusterComplexName(kubeClusterName, AirshipDefaultClusterType)
}
// kubeClusterName matches the format myCluster_something.
// Let's check if "something" is a clusterType.
potentialType := parts[len(parts)-1]
for _, ct := range AllClusterTypes {
if potentialType == ct {
// Rejoin the parts in the case of "my_cluster_etc_etc_<clusterType>"
name := strings.Join(parts[:len(parts)-1], AirshipClusterNameSeparator)
return NewClusterComplexName(name, potentialType)
}
}
// "something" is not a valid clusterType, so just use the default
return NewClusterComplexName(kubeClusterName, AirshipDefaultClusterType)
}

View File

@ -1,72 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/testutil"
)
func TestPrettyString(t *testing.T) {
fSys := testutil.SetupTestFs(t, "testdata")
data, err := fSys.ReadFile("/prettycluster-string.yaml")
require.NoError(t, err)
cluster := testutil.DummyCluster()
assert.EqualValues(t, cluster.PrettyString(), string(data))
}
func TestValidClusterTypeFail(t *testing.T) {
err := config.ValidClusterType("Fake")
assert.Error(t, err)
}
func TestGetCluster(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
cluster, err := conf.GetCluster("def", config.Ephemeral)
require.NoError(t, err)
// Test Positives
assert.EqualValues(t, cluster.NameInKubeconf, "def_ephemeral")
assert.EqualValues(t, cluster.KubeCluster().Server, "http://5.6.7.8")
// Test Wrong Cluster
_, err = conf.GetCluster("unknown", config.Ephemeral)
assert.Error(t, err)
// Test Wrong Cluster Type
_, err = conf.GetCluster("def", "Unknown")
assert.Error(t, err)
}
func TestAddCluster(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
co := testutil.DummyClusterOptions()
cluster, err := conf.AddCluster(co)
require.NoError(t, err)
assert.EqualValues(t, conf.Clusters[co.Name].ClusterTypes[co.ClusterType], cluster)
}

View File

@ -1,6 +1,4 @@
/* /*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -47,12 +45,6 @@ type Config struct {
// +optional // +optional
APIVersion string `json:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
// Clusters is a map of referenceable names to cluster configs
Clusters map[string]*ClusterPurpose `json:"clusters"`
// AuthInfos is a map of referenceable names to user configs
AuthInfos map[string]*AuthInfo `json:"users"`
// Permissions is a struct of permissions for file and directory // Permissions is a struct of permissions for file and directory
Permissions Permissions `json:"permissions,omitempty"` Permissions Permissions `json:"permissions,omitempty"`
@ -124,9 +116,6 @@ func CreateConfig(airshipConfigPath string, kubeConfigPath string) error {
cfg := NewConfig() cfg := NewConfig()
cfg.kubeConfig = NewKubeConfig() cfg.kubeConfig = NewKubeConfig()
cfg.initConfigPath(airshipConfigPath, kubeConfigPath) cfg.initConfigPath(airshipConfigPath, kubeConfigPath)
if err := cfg.reconcileConfig(); err != nil {
return err
}
return cfg.PersistConfig(true) return cfg.PersistConfig(true)
} }
@ -170,8 +159,7 @@ func (c *Config) LoadConfig(airshipConfigPath, kubeConfigPath string, create boo
return err return err
} }
// Lets navigate through the kubeconfig to populate the references in airship config return nil
return c.reconcileConfig()
} }
// loadFromAirConfig populates the Config from the file found at airshipConfigPath. // loadFromAirConfig populates the Config from the file found at airshipConfigPath.
@ -217,220 +205,14 @@ func (c *Config) loadKubeConfig(kubeConfigPath string, create bool) error {
return err return err
} }
// reconcileConfig serves two functions:
// 1 - it will consume from kubeconfig and update airship config
// For cluster that do not comply with the airship cluster type expectations a default
// behavior will be implemented. Such as ,by default they will be tar or ephemeral
// 2 - it will update kubeconfig cluster objects with the appropriate <clustername>_<clustertype> convention
func (c *Config) reconcileConfig() error {
updatedClusterNames, persistIt := c.reconcileClusters()
c.reconcileContexts(updatedClusterNames)
c.reconcileAuthInfos()
c.reconcileCurrentContext()
// I changed things during the reconciliation
// Lets reflect them in the config files
// Specially useful if the config is loaded during a get operation
// If it was a Set this would have happened eventually any way
if persistIt {
return c.PersistConfig(true)
}
return nil
}
// reconcileClusters synchronizes the airshipconfig file with the kubeconfig file.
//
// It iterates over the clusters listed in the kubeconfig. If any cluster in
// the kubeconfig does not meet the <name>_<type> convention, the name is
// first changed to the airship default.
//
// It then updates the airshipconfig's names of those clusters, as well as the
// pointer to the clusters.
// If the cluster wasn't referenced prior to the call, it is created; otherwise
// it is modified.
//
// Finally, any clusters listed in the airshipconfig that are no longer
// referenced in the kubeconfig are deleted
//
// The function returns a mapping of changed names in the kubeconfig, as well
// as a boolean denoting that the config files need to be written to file
func (c *Config) reconcileClusters() (map[string]string, bool) {
// updatedClusterNames is a mapping from OLD cluster names to NEW
// cluster names. This will be used later when we update contexts
updatedClusterNames := map[string]string{}
persistIt := false
for clusterName, cluster := range c.kubeConfig.Clusters {
clusterComplexName := NewClusterComplexNameFromKubeClusterName(clusterName)
// Check if the cluster from the kubeconfig file complies with
// the airship naming convention
if clusterName != clusterComplexName.String() {
// Update the kubeconfig with proper airship name
c.kubeConfig.Clusters[clusterComplexName.String()] = cluster
delete(c.kubeConfig.Clusters, clusterName)
// We also need to save the mapping from the old name
// so we can update the context in the kubeconfig later
updatedClusterNames[clusterName] = clusterComplexName.String()
// Since we've modified the kubeconfig object, we'll
// need to let the caller know that the kubeconfig file
// needs to be updated
persistIt = true
// Otherwise this is a cluster that didnt have an
// airship cluster type, however when you added the
// cluster type
// Probable should just add a number _<COUNTER to it
}
// The cluster in the kubeconfig is not present in the airship config. Create it.
if c.Clusters[clusterComplexName.Name] == nil {
c.Clusters[clusterComplexName.Name] = NewClusterPurpose()
}
// NOTE(drewwalters96): This is a user error because a cluster is defined in name but incomplete. We
// need to fail sooner than this function; add up-front validation for this later.
if c.Clusters[clusterComplexName.Name].ClusterTypes == nil {
c.Clusters[clusterComplexName.Name].ClusterTypes = make(map[string]*Cluster)
}
// The cluster is defined, but the type is not. Define the type.
if c.Clusters[clusterComplexName.Name].ClusterTypes[clusterComplexName.Type] == nil {
c.Clusters[clusterComplexName.Name].ClusterTypes[clusterComplexName.Type] = NewCluster()
}
// Point cluster at kubeconfig
configCluster := c.Clusters[clusterComplexName.Name].ClusterTypes[clusterComplexName.Type]
configCluster.NameInKubeconf = clusterComplexName.String()
// Store the reference to the KubeConfig Cluster in the Airship Config
configCluster.SetKubeCluster(cluster)
}
persistIt = c.rmConfigClusterStragglers(persistIt)
return updatedClusterNames, persistIt
}
// Removes Cluster configuration that exist in Airship Config and do not have
// any kubeconfig appropriate <clustername>_<clustertype> entries
func (c *Config) rmConfigClusterStragglers(persistIt bool) bool {
rccs := persistIt
// Checking if there is any Cluster reference in airship config that does not match
// an actual Cluster struct in kubeconfig
for clusterName := range c.Clusters {
for cType, cluster := range c.Clusters[clusterName].ClusterTypes {
if _, found := c.kubeConfig.Clusters[cluster.NameInKubeconf]; !found {
// Instead of removing it , I could add a empty entry in kubeconfig as well
// Will see what is more appropriate with use of Modules configuration
delete(c.Clusters[clusterName].ClusterTypes, cType)
// If that was the last cluster type, then we
// should delete the cluster entry
if len(c.Clusters[clusterName].ClusterTypes) == 0 {
delete(c.Clusters, clusterName)
}
rccs = true
}
}
}
return rccs
}
func (c *Config) reconcileContexts(updatedClusterNames map[string]string) {
for key, context := range c.kubeConfig.Contexts {
// Check if the Cluster name referred to by the context
// was updated during the cluster reconcile
if newName, ok := updatedClusterNames[context.Cluster]; ok {
context.Cluster = newName
}
if c.Contexts[key] == nil {
c.Contexts[key] = NewContext()
}
// Make sure the name matches
c.Contexts[key].NameInKubeconf = context.Cluster
c.Contexts[key].SetKubeContext(context)
// What about if a Context refers to a cluster that does not
// exist in airship config
clusterName := NewClusterComplexNameFromKubeClusterName(context.Cluster)
if c.Clusters[clusterName.Name] == nil {
// I cannot create this cluster, it will have empty information
// Best course of action is to delete it I think
delete(c.kubeConfig.Contexts, key)
}
}
// Checking if there is any Context reference in airship config that does not match
// an actual Context struct in kubeconfig, if they do not exists I will delete
// Since context in airship config are only references mainly.
for key := range c.Contexts {
if c.kubeConfig.Contexts[key] == nil {
delete(c.Contexts, key)
}
}
}
func (c *Config) reconcileAuthInfos() {
for key, authinfo := range c.kubeConfig.AuthInfos {
// Simple check if the AuthInfo name is referenced in airship config
if c.AuthInfos[key] == nil && authinfo != nil {
// Add the reference
c.AuthInfos[key] = NewAuthInfo()
}
c.AuthInfos[key].authInfo = authinfo
}
// Checking if there is any AuthInfo reference in airship config that does not match
// an actual Auth Info struct in kubeconfig
for key := range c.AuthInfos {
if c.kubeConfig.AuthInfos[key] == nil {
delete(c.AuthInfos, key)
}
}
}
func (c *Config) reconcileCurrentContext() {
// If the Airship current context is different that the current context in the kubeconfig
// then
// - if the airship current context is valid, then updated kubeconfig CC
// - if the airship currentcontext is invalid, and the kubeconfig CC is valid, then create the reference
// - otherwise , they are both empty. Make sure
if c.Contexts[c.CurrentContext] == nil { // Its not valid
if c.Contexts[c.kubeConfig.CurrentContext] != nil {
c.CurrentContext = c.kubeConfig.CurrentContext
}
} else {
// Overpowers kubeConfig CurrentContext
if c.kubeConfig.CurrentContext != c.CurrentContext {
c.kubeConfig.CurrentContext = c.CurrentContext
}
}
}
// EnsureComplete verifies that a Config object is ready to use. // EnsureComplete verifies that a Config object is ready to use.
// A complete Config object meets the following criteria: // A complete Config object meets the following criteria:
// * At least 1 Cluster is defined
// * At least 1 AuthInfo (user) is defined
// * At least 1 Context is defined // * At least 1 Context is defined
// * At least 1 Manifest is defined // * At least 1 Manifest is defined
// * The CurrentContext is set // * The CurrentContext is set
// * The CurrentContext identifies an existing Context // * The CurrentContext identifies an existing Context
// * The CurrentContext identifies an existing Manifest // * The CurrentContext identifies an existing Manifest
func (c *Config) EnsureComplete() error { func (c *Config) EnsureComplete() error {
if len(c.Clusters) == 0 {
return ErrMissingConfig{
What: "At least one cluster needs to be defined",
}
}
if len(c.AuthInfos) == 0 {
return ErrMissingConfig{
What: "At least one Authentication Information (User) needs to be defined",
}
}
if len(c.Contexts) == 0 { if len(c.Contexts) == 0 {
return ErrMissingConfig{ return ErrMissingConfig{
What: "At least one Context needs to be defined", What: "At least one Context needs to be defined",
@ -560,113 +342,6 @@ func (c *Config) SetKubeConfig(kubeConfig *clientcmdapi.Config) {
c.kubeConfig = kubeConfig c.kubeConfig = kubeConfig
} }
// GetCluster returns a cluster instance
func (c *Config) GetCluster(cName, cType string) (*Cluster, error) {
_, exists := c.Clusters[cName]
if !exists {
return nil, ErrMissingConfig{What: fmt.Sprintf("Cluster with name '%s' of type '%s'", cName, cType)}
}
// Alternative to this would be enhance Cluster.String() to embed the appropriate kubeconfig cluster information
cluster, exists := c.Clusters[cName].ClusterTypes[cType]
if !exists {
return nil, ErrMissingConfig{What: fmt.Sprintf("Cluster with name '%s' of type '%s'", cName, cType)}
}
return cluster, nil
}
// AddCluster creates a new cluster and returns the
// newly created cluster object
func (c *Config) AddCluster(theCluster *ClusterOptions) (*Cluster, error) {
// Need to create new cluster placeholder
// Get list of ClusterPurposes that match the theCluster.name
// Cluster might exists, but ClusterPurpose should not
_, exists := c.Clusters[theCluster.Name]
if !exists {
c.Clusters[theCluster.Name] = NewClusterPurpose()
}
// Create the new Airship config Cluster
nCluster := NewCluster()
c.Clusters[theCluster.Name].ClusterTypes[theCluster.ClusterType] = nCluster
// Create a new KubeConfig Cluster object as well
kcluster := clientcmdapi.NewCluster()
clusterName := NewClusterComplexName(theCluster.Name, theCluster.ClusterType)
nCluster.NameInKubeconf = clusterName.String()
nCluster.SetKubeCluster(kcluster)
c.KubeConfig().Clusters[clusterName.String()] = kcluster
// Ok , I have initialized structs for the Cluster information
// We can use Modify to populate the correct information
return c.ModifyCluster(nCluster, theCluster)
}
// ModifyCluster updates cluster object with given cluster options
func (c *Config) ModifyCluster(cluster *Cluster, theCluster *ClusterOptions) (*Cluster, error) {
kcluster := cluster.KubeCluster()
if kcluster == nil {
return cluster, nil
}
if theCluster.Server != "" {
kcluster.Server = theCluster.Server
}
if theCluster.InsecureSkipTLSVerify {
kcluster.InsecureSkipTLSVerify = theCluster.InsecureSkipTLSVerify
// Specifying insecur mode clears any certificate authority
if kcluster.InsecureSkipTLSVerify {
kcluster.CertificateAuthority = ""
kcluster.CertificateAuthorityData = nil
}
}
if theCluster.CertificateAuthority == "" {
return cluster, nil
}
if theCluster.EmbedCAData {
readData, err := ioutil.ReadFile(theCluster.CertificateAuthority)
kcluster.CertificateAuthorityData = readData
if err != nil {
return cluster, err
}
kcluster.InsecureSkipTLSVerify = false
kcluster.CertificateAuthority = ""
} else {
caPath, err := filepath.Abs(theCluster.CertificateAuthority)
if err != nil {
return cluster, err
}
kcluster.CertificateAuthority = caPath
// Specifying a certificate authority file clears certificate authority data and insecure mode
if caPath != "" {
kcluster.InsecureSkipTLSVerify = false
kcluster.CertificateAuthorityData = nil
}
}
return cluster, nil
}
// GetClusters returns all of the clusters associated with the Config sorted by name
func (c *Config) GetClusters() []*Cluster {
keys := make([]string, 0, len(c.Clusters))
for name := range c.Clusters {
keys = append(keys, name)
}
sort.Strings(keys)
clusters := make([]*Cluster, 0, len(c.Clusters))
for _, name := range keys {
for _, clusterType := range AllClusterTypes {
cluster, exists := c.Clusters[name].ClusterTypes[clusterType]
if exists {
// If it doesn't exist, then there must not be
// a cluster with this name/type combination.
// This is expected behavior
clusters = append(clusters, cluster)
}
}
}
return clusters
}
// GetContext returns a context instance // GetContext returns a context instance
func (c *Config) GetContext(cName string) (*Context, error) { func (c *Config) GetContext(cName string) (*Context, error) {
context, exists := c.Contexts[cName] context, exists := c.Contexts[cName]
@ -707,30 +382,19 @@ func (c *Config) AddContext(theContext *ContextOptions) *Context {
// Create the new Airship config context // Create the new Airship config context
nContext := NewContext() nContext := NewContext()
c.Contexts[theContext.Name] = nContext c.Contexts[theContext.Name] = nContext
// Create a new KubeConfig Context object as well
context := clientcmdapi.NewContext()
nContext.NameInKubeconf = theContext.Name nContext.NameInKubeconf = theContext.Name
nContext.SetKubeContext(context)
c.KubeConfig().Contexts[theContext.Name] = context
// Ok , I have initialized structs for the Context information // Ok , I have initialized structs for the Context information
// We can use Modify to populate the correct information // We can use Modify to populate the correct information
c.ModifyContext(nContext, theContext) c.ModifyContext(nContext, theContext)
nContext.ClusterType()
return nContext return nContext
} }
// ModifyContext updates Context object with given given context options // ModifyContext updates Context object with given given context options
func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) { func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) {
kubeContext := context.KubeContext() if theContext.ManagementConfiguration != "" {
if kubeContext == nil { context.ManagementConfiguration = theContext.ManagementConfiguration
return
}
if theContext.Cluster != "" {
kubeContext.Cluster = theContext.Cluster
}
if theContext.AuthInfo != "" {
kubeContext.AuthInfo = theContext.AuthInfo
} }
if theContext.Manifest != "" { if theContext.Manifest != "" {
context.Manifest = theContext.Manifest context.Manifest = theContext.Manifest
@ -738,16 +402,11 @@ func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) {
if theContext.EncryptionConfig != "" { if theContext.EncryptionConfig != "" {
context.EncryptionConfig = theContext.EncryptionConfig context.EncryptionConfig = theContext.EncryptionConfig
} }
if theContext.Namespace != "" {
kubeContext.Namespace = theContext.Namespace
}
} }
// GetCurrentContext methods Returns the appropriate information for the current context // GetCurrentContext methods Returns the appropriate information for the current context
// Current Context holds labels for the approriate config objects // Current Context holds labels for the approriate config objects
// Cluster is the name of the cluster for this context
// ClusterType is the name of the clustertype for this context, it should be a flag we pass to it?? // ClusterType is the name of the clustertype for this context, it should be a flag we pass to it??
// AuthInfo is the name of the authInfo for this context
// Manifest is the default manifest to be use with this context // Manifest is the default manifest to be use with this context
// Purpose for this method is simplifying the current context information // Purpose for this method is simplifying the current context information
func (c *Config) GetCurrentContext() (*Context, error) { func (c *Config) GetCurrentContext() (*Context, error) {
@ -759,27 +418,6 @@ func (c *Config) GetCurrentContext() (*Context, error) {
return currentContext, nil return currentContext, nil
} }
// CurrentContextCluster returns the Cluster for the current context
func (c *Config) CurrentContextCluster() (*Cluster, error) {
currentContext, err := c.GetCurrentContext()
if err != nil {
return nil, err
}
clusterName := NewClusterComplexNameFromKubeClusterName(currentContext.KubeContext().Cluster)
return c.Clusters[clusterName.Name].ClusterTypes[currentContext.ClusterType()], nil
}
// CurrentContextAuthInfo returns the AuthInfo for the current context
func (c *Config) CurrentContextAuthInfo() (*AuthInfo, error) {
currentContext, err := c.GetCurrentContext()
if err != nil {
return nil, err
}
return c.AuthInfos[currentContext.KubeContext().AuthInfo], nil
}
// CurrentContextManifest returns the manifest for the current context // CurrentContextManifest returns the manifest for the current context
func (c *Config) CurrentContextManifest() (*Manifest, error) { func (c *Config) CurrentContextManifest() (*Manifest, error) {
currentContext, err := c.GetCurrentContext() currentContext, err := c.GetCurrentContext()
@ -798,10 +436,6 @@ func (c *Config) CurrentContextEntryPoint(phase string) (string, error) {
return "", err return "", err
} }
err = ValidClusterType(clusterType)
if err != nil {
return "", err
}
ccm, err := c.CurrentContextManifest() ccm, err := c.CurrentContextManifest()
if err != nil { if err != nil {
return "", err return "", err
@ -844,167 +478,6 @@ func (c *Config) CurrentContextClusterName() (string, error) {
return context.ClusterName(), nil return context.ClusterName(), nil
} }
// GetAuthInfo returns an instance of authino
// Credential or AuthInfo related methods
func (c *Config) GetAuthInfo(aiName string) (*AuthInfo, error) {
authinfo, exists := c.AuthInfos[aiName]
if !exists {
return nil, ErrMissingConfig{What: fmt.Sprintf("User credentials with name '%s'", aiName)}
}
decodedAuthInfo, err := DecodeAuthInfo(authinfo.authInfo)
if err != nil {
return nil, err
}
authinfo.authInfo = decodedAuthInfo
return authinfo, nil
}
// GetAuthInfos returns a slice containing all the AuthInfos associated with
// the Config sorted by name
func (c *Config) GetAuthInfos() ([]*AuthInfo, error) {
keys := make([]string, 0, len(c.AuthInfos))
for name := range c.AuthInfos {
keys = append(keys, name)
}
sort.Strings(keys)
authInfos := make([]*AuthInfo, 0, len(c.AuthInfos))
for _, name := range keys {
decodedAuthInfo, err := DecodeAuthInfo(c.AuthInfos[name].authInfo)
if err != nil {
return []*AuthInfo{}, err
}
c.AuthInfos[name].authInfo = decodedAuthInfo
authInfos = append(authInfos, c.AuthInfos[name])
}
return authInfos, nil
}
// AddAuthInfo creates new AuthInfo with context details updated
// in the airship config and kube config
func (c *Config) AddAuthInfo(theAuthInfo *AuthInfoOptions) *AuthInfo {
// Create the new Airship config context
nAuthInfo := NewAuthInfo()
c.AuthInfos[theAuthInfo.Name] = nAuthInfo
// Create a new KubeConfig AuthInfo object as well
authInfo := clientcmdapi.NewAuthInfo()
nAuthInfo.authInfo = authInfo
c.KubeConfig().AuthInfos[theAuthInfo.Name] = authInfo
c.ModifyAuthInfo(nAuthInfo, theAuthInfo)
return nAuthInfo
}
// ModifyAuthInfo updates the AuthInfo in the Config object
func (c *Config) ModifyAuthInfo(authinfo *AuthInfo, theAuthInfo *AuthInfoOptions) {
kubeAuthInfo := EncodeAuthInfo(authinfo.KubeAuthInfo())
if kubeAuthInfo == nil {
return
}
if theAuthInfo.ClientCertificate != "" {
kubeAuthInfo.ClientCertificate = EncodeString(theAuthInfo.ClientCertificate)
}
if theAuthInfo.Token != "" {
kubeAuthInfo.Token = EncodeString(theAuthInfo.Token)
}
if theAuthInfo.Username != "" {
kubeAuthInfo.Username = theAuthInfo.Username
}
if theAuthInfo.Password != "" {
kubeAuthInfo.Password = EncodeString(theAuthInfo.Password)
}
if theAuthInfo.ClientKey != "" {
kubeAuthInfo.ClientKey = EncodeString(theAuthInfo.ClientKey)
}
}
// ImportFromKubeConfig absorbs the clusters, contexts and credentials from the
// given kubeConfig
func (c *Config) ImportFromKubeConfig(kubeConfigPath string) error {
_, err := os.Stat(kubeConfigPath)
if err != nil {
return err
}
kubeConfig, err := clientcmd.LoadFromFile(kubeConfigPath)
if err != nil {
return err
}
c.importClusters(kubeConfig)
c.importContexts(kubeConfig)
c.importAuthInfos(kubeConfig)
return c.PersistConfig(true)
}
func (c *Config) importClusters(importKubeConfig *clientcmdapi.Config) {
for clusterName, cluster := range importKubeConfig.Clusters {
clusterComplexName := NewClusterComplexNameFromKubeClusterName(clusterName)
if _, err := c.GetCluster(clusterComplexName.Name, clusterComplexName.Type); err == nil {
// err == nil implies that we were successfully able to
// get the cluster from the existing configuration.
// Since existing clusters takes precedence, skip this cluster
continue
}
// Initialize the new cluster for the airship configuration
airshipCluster := NewCluster()
airshipCluster.NameInKubeconf = clusterComplexName.String()
// Store the reference to the KubeConfig Cluster in the Airship Config
airshipCluster.SetKubeCluster(cluster)
// Update the airship configuration
if _, ok := c.Clusters[clusterComplexName.Name]; !ok {
c.Clusters[clusterComplexName.Name] = NewClusterPurpose()
}
c.Clusters[clusterComplexName.Name].ClusterTypes[clusterComplexName.Type] = airshipCluster
c.kubeConfig.Clusters[clusterComplexName.String()] = cluster
}
}
func (c *Config) importContexts(importKubeConfig *clientcmdapi.Config) {
// TODO(howell): This function doesn't handle the case when an incoming
// context refers to a cluster that doesn't exist in the airship
// configuration.
for kubeContextName, kubeContext := range importKubeConfig.Contexts {
if _, ok := c.kubeConfig.Contexts[kubeContextName]; ok {
// Since existing contexts take precedence, skip this context
continue
}
clusterComplexName := NewClusterComplexNameFromKubeClusterName(kubeContext.Cluster)
if kubeContext.Cluster != clusterComplexName.String() {
// If the name of cluster from the kubeConfig doesn't
// match the clusterComplexName, it needs to be updated
kubeContext.Cluster = clusterComplexName.String()
}
airshipContext, ok := c.Contexts[kubeContextName]
if !ok {
airshipContext = NewContext()
}
airshipContext.NameInKubeconf = kubeContext.Cluster
airshipContext.Manifest = AirshipDefaultManifest
airshipContext.SetKubeContext(kubeContext)
// Store the contexts in the airship configuration
c.Contexts[kubeContextName] = airshipContext
c.kubeConfig.Contexts[kubeContextName] = kubeContext
}
}
func (c *Config) importAuthInfos(importKubeConfig *clientcmdapi.Config) {
for key, authinfo := range importKubeConfig.AuthInfos {
if _, ok := c.AuthInfos[key]; ok {
// Since existing credentials take precedence, skip this credential
continue
}
c.AuthInfos[key] = NewAuthInfo()
c.AuthInfos[key].SetKubeAuthInfo(authinfo)
c.kubeConfig.AuthInfos[key] = authinfo
}
}
// GetManifests returns all of the Manifests associated with the Config sorted by name // GetManifests returns all of the Manifests associated with the Config sorted by name
func (c *Config) GetManifests() []*Manifest { func (c *Config) GetManifests() []*Manifest {
keys := make([]string, 0, len(c.Manifests)) keys := make([]string, 0, len(c.Manifests))
@ -1164,20 +637,20 @@ func (c *Config) ModifyEncryptionConfig(encryptionConfig *EncryptionConfig, opti
// CurrentContextManagementConfig returns the management options for the current context // CurrentContextManagementConfig returns the management options for the current context
func (c *Config) CurrentContextManagementConfig() (*ManagementConfiguration, error) { func (c *Config) CurrentContextManagementConfig() (*ManagementConfiguration, error) {
currentCluster, err := c.CurrentContextCluster() currentContext, err := c.GetCurrentContext()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if currentCluster.ManagementConfiguration == "" { if currentContext.ManagementConfiguration == "" {
return nil, ErrMissingConfig{ return nil, ErrMissingConfig{
What: fmt.Sprintf("No management config listed for cluster %s", currentCluster.NameInKubeconf), What: fmt.Sprintf("No management config listed for cluster %s", currentContext.NameInKubeconf),
} }
} }
managementCfg, exists := c.ManagementConfiguration[currentCluster.ManagementConfiguration] managementCfg, exists := c.ManagementConfiguration[currentContext.ManagementConfiguration]
if !exists { if !exists {
return nil, ErrMissingManagementConfiguration{cluster: currentCluster} return nil, ErrMissingManagementConfiguration{context: currentContext}
} }
return managementCfg, nil return managementCfg, nil
@ -1205,44 +678,3 @@ func (c *Config) CurrentContextManifestMetadata() (*Metadata, error) {
} }
return meta, nil return meta, nil
} }
// DecodeAuthInfo returns authInfo with credentials decoded
func DecodeAuthInfo(authinfo *clientcmdapi.AuthInfo) (*clientcmdapi.AuthInfo, error) {
password := authinfo.Password
decodedPassword, err := DecodeString(password)
if err != nil {
return nil, ErrDecodingCredentials{Given: password}
}
authinfo.Password = decodedPassword
token := authinfo.Token
decodedToken, err := DecodeString(token)
if err != nil {
return nil, ErrDecodingCredentials{Given: token}
}
authinfo.Token = decodedToken
clientCert := authinfo.ClientCertificate
decodedClientCertificate, err := DecodeString(clientCert)
if err != nil {
return nil, ErrDecodingCredentials{Given: clientCert}
}
authinfo.ClientCertificate = decodedClientCertificate
clientKey := authinfo.ClientKey
decodedClientKey, err := DecodeString(clientKey)
if err != nil {
return nil, ErrDecodingCredentials{Given: clientKey}
}
authinfo.ClientKey = decodedClientKey
return authinfo, nil
}
// EncodeAuthInfo returns authInfo with credentials base64 encoded
func EncodeAuthInfo(authinfo *clientcmdapi.AuthInfo) *clientcmdapi.AuthInfo {
authinfo.Password = EncodeString(authinfo.Password)
authinfo.Token = EncodeString(authinfo.Token)
authinfo.ClientCertificate = EncodeString(authinfo.ClientCertificate)
authinfo.ClientKey = EncodeString(authinfo.ClientKey)
return authinfo
}

View File

@ -20,86 +20,6 @@ import (
"errors" "errors"
) )
// RunSetAuthInfo validates the given command line options and invokes AddAuthInfo/ModifyAuthInfo
func RunSetAuthInfo(o *AuthInfoOptions, airconfig *Config, writeToStorage bool) (bool, error) {
modified := false
err := o.Validate()
if err != nil {
return modified, err
}
authinfo, err := airconfig.GetAuthInfo(o.Name)
if err != nil {
var cerr ErrMissingConfig
if !errors.As(err, &cerr) {
// An error occurred, but it wasn't a "missing" config error.
return modified, err
}
// authinfo didn't exist, create it
// ignoring the returned added authinfo
airconfig.AddAuthInfo(o)
} else {
// AuthInfo exists, lets update
airconfig.ModifyAuthInfo(authinfo, o)
modified = true
}
// Update configuration file just in time persistence approach
if writeToStorage {
if err := airconfig.PersistConfig(true); err != nil {
// Error that it didnt persist the changes
return modified, ErrConfigFailed{}
}
}
return modified, nil
}
// RunSetCluster validates the given command line options and invokes AddCluster/ModifyCluster
func RunSetCluster(o *ClusterOptions, airconfig *Config, writeToStorage bool) (bool, error) {
modified := false
err := o.Validate()
if err != nil {
return modified, err
}
cluster, err := airconfig.GetCluster(o.Name, o.ClusterType)
if err != nil {
var cerr ErrMissingConfig
if !errors.As(err, &cerr) {
// An error occurred, but it wasn't a "missing" config error.
return modified, err
}
// Cluster didn't exist, create it
_, err := airconfig.AddCluster(o)
if err != nil {
return modified, err
}
modified = false
} else {
// Cluster exists, lets update
_, err := airconfig.ModifyCluster(cluster, o)
if err != nil {
return modified, err
}
modified = true
}
// Update configuration file
// Just in time persistence approach
if writeToStorage {
if err := airconfig.PersistConfig(true); err != nil {
// Some warning here , that it didnt persist the changes because of this
// Or should we float this up
// What would it mean? No value.
return modified, err
}
}
return modified, nil
}
// RunSetContext validates the given command line options and invokes AddContext/ModifyContext // RunSetContext validates the given command line options and invokes AddContext/ModifyContext
func RunSetContext(o *ContextOptions, airconfig *Config, writeToStorage bool) (bool, error) { func RunSetContext(o *ContextOptions, airconfig *Config, writeToStorage bool) (bool, error) {
modified := false modified := false

View File

@ -25,61 +25,6 @@ import (
"opendev.org/airship/airshipctl/testutil" "opendev.org/airship/airshipctl/testutil"
) )
func TestRunSetAuthInfo(t *testing.T) {
t.Run("testAddAuthInfo", func(t *testing.T) {
conf := testutil.DummyConfig()
dummyAuthInfoOptions := testutil.DummyAuthInfoOptions()
dummyAuthInfoOptions.Name = "second_user"
dummyAuthInfoOptions.Token = ""
modified, err := config.RunSetAuthInfo(dummyAuthInfoOptions, conf, false)
assert.NoError(t, err)
assert.False(t, modified)
assert.Contains(t, conf.AuthInfos, "second_user")
})
t.Run("testModifyAuthInfo", func(t *testing.T) {
conf := testutil.DummyConfig()
dummyAuthInfoOptions := testutil.DummyAuthInfoOptions()
dummyAuthInfoOptions.Name = "dummy_user"
dummyAuthInfoOptions.Password = "testpassword123"
dummyAuthInfoOptions.Token = ""
modified, err := config.RunSetAuthInfo(dummyAuthInfoOptions, conf, false)
assert.NoError(t, err)
assert.True(t, modified)
authInfo, err := conf.GetAuthInfo("dummy_user")
assert.NoError(t, err)
assert.Equal(t, dummyAuthInfoOptions.Password, authInfo.KubeAuthInfo().Password)
})
}
func TestRunSetCluster(t *testing.T) {
t.Run("testAddCluster", func(t *testing.T) {
conf := testutil.DummyConfig()
dummyClusterOptions := testutil.DummyClusterOptions()
dummyClusterOptions.Name = "second_cluster"
modified, err := config.RunSetCluster(dummyClusterOptions, conf, false)
assert.NoError(t, err)
assert.False(t, modified)
assert.Contains(t, conf.Clusters, "second_cluster")
})
t.Run("testModifyCluster", func(t *testing.T) {
conf := testutil.DummyConfig()
dummyClusterOptions := testutil.DummyClusterOptions()
dummyClusterOptions.Server = "http://123.45.67.890"
modified, err := config.RunSetCluster(dummyClusterOptions, conf, false)
assert.NoError(t, err)
assert.True(t, modified)
assert.Equal(
t, "http://123.45.67.890",
conf.Clusters["dummy_cluster"].ClusterTypes["ephemeral"].KubeCluster().Server)
})
}
func TestRunSetContext(t *testing.T) { func TestRunSetContext(t *testing.T) {
t.Run("testAddContext", func(t *testing.T) { t.Run("testAddContext", func(t *testing.T) {
conf := testutil.DummyConfig() conf := testutil.DummyConfig()
@ -95,12 +40,10 @@ func TestRunSetContext(t *testing.T) {
t.Run("testModifyContext", func(t *testing.T) { t.Run("testModifyContext", func(t *testing.T) {
conf := testutil.DummyConfig() conf := testutil.DummyConfig()
dummyContextOptions := testutil.DummyContextOptions() dummyContextOptions := testutil.DummyContextOptions()
dummyContextOptions.Namespace = "new_namespace"
modified, err := config.RunSetContext(dummyContextOptions, conf, false) modified, err := config.RunSetContext(dummyContextOptions, conf, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, modified) assert.True(t, modified)
assert.Equal(t, "new_namespace", conf.Contexts["dummy_context"].KubeContext().Namespace)
}) })
} }

View File

@ -18,9 +18,7 @@ package config_test
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
@ -35,10 +33,6 @@ const (
stringDelta = "_changed" stringDelta = "_changed"
currentContextName = "def_ephemeral" currentContextName = "def_ephemeral"
defaultString = "default" defaultString = "default"
newToken = "dummy_token_changed"
newPassword = "dummy_password_changed"
newCertificate = "dummy_certificate_changed"
newKey = "dummy_key_changed"
) )
func TestString(t *testing.T) { func TestString(t *testing.T) {
@ -56,14 +50,6 @@ func TestString(t *testing.T) {
name: "context", name: "context",
stringer: testutil.DummyContext(), stringer: testutil.DummyContext(),
}, },
{
name: "cluster",
stringer: testutil.DummyCluster(),
},
{
name: "authinfo",
stringer: testutil.DummyAuthInfo(),
},
{ {
name: "manifest", name: "manifest",
stringer: testutil.DummyManifest(), stringer: testutil.DummyManifest(),
@ -106,11 +92,7 @@ func TestLoadConfig(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
assert.Len(t, conf.Clusters, 6) assert.Len(t, conf.Contexts, 4)
require.Contains(t, conf.Clusters, "def")
assert.Len(t, conf.Clusters["def"].ClusterTypes, 2)
assert.Len(t, conf.Contexts, 3)
assert.Len(t, conf.AuthInfos, 3)
} }
func TestPersistConfig(t *testing.T) { func TestPersistConfig(t *testing.T) {
@ -124,13 +106,7 @@ func TestPersistConfig(t *testing.T) {
assert.FileExists(t, conf.LoadedConfigPath()) assert.FileExists(t, conf.LoadedConfigPath())
assert.FileExists(t, conf.KubeConfigPath()) assert.FileExists(t, conf.KubeConfigPath())
// Check that the invalid name was changed to a valid one // Check that the invalid name was changed to a valid one
assert.Contains(t, conf.KubeConfig().Clusters, "invalidName_target") assert.Contains(t, conf.KubeConfig().Clusters, "def_ephemeral")
// Check that the missing cluster was added to the airshipconfig
assert.Contains(t, conf.Clusters, "onlyinkubeconf")
// Check that the "stragglers" were removed from the airshipconfig
assert.NotContains(t, conf.Clusters, "straggler")
} }
func TestEnsureComplete(t *testing.T) { func TestEnsureComplete(t *testing.T) {
@ -143,33 +119,9 @@ func TestEnsureComplete(t *testing.T) {
config config.Config config config.Config
expectedErr error expectedErr error
}{ }{
{
name: "no clusters defined",
config: config.Config{
Clusters: map[string]*config.ClusterPurpose{},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"testManifest": {}},
CurrentContext: "testContext",
},
expectedErr: config.ErrMissingConfig{What: "At least one cluster needs to be defined"},
},
{
name: "no users defined",
config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"testManifest": {}},
CurrentContext: "testContext",
},
expectedErr: config.ErrMissingConfig{What: "At least one Authentication Information (User) needs to be defined"},
},
{ {
name: "no contexts defined", name: "no contexts defined",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{}, Contexts: map[string]*config.Context{},
Manifests: map[string]*config.Manifest{"testManifest": {}}, Manifests: map[string]*config.Manifest{"testManifest": {}},
CurrentContext: "testContext", CurrentContext: "testContext",
@ -179,8 +131,6 @@ func TestEnsureComplete(t *testing.T) {
{ {
name: "no manifests defined", name: "no manifests defined",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{}, Manifests: map[string]*config.Manifest{},
CurrentContext: "testContext", CurrentContext: "testContext",
@ -190,8 +140,6 @@ func TestEnsureComplete(t *testing.T) {
{ {
name: "current context not defined", name: "current context not defined",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"testManifest": {}}, Manifests: map[string]*config.Manifest{"testManifest": {}},
CurrentContext: "", CurrentContext: "",
@ -201,8 +149,6 @@ func TestEnsureComplete(t *testing.T) {
{ {
name: "no context for current context", name: "no context for current context",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"DIFFERENT_CONTEXT": {Manifest: "testManifest"}}, Contexts: map[string]*config.Context{"DIFFERENT_CONTEXT": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"testManifest": {}}, Manifests: map[string]*config.Manifest{"testManifest": {}},
CurrentContext: "testContext", CurrentContext: "testContext",
@ -212,8 +158,6 @@ func TestEnsureComplete(t *testing.T) {
{ {
name: "no manifest for current context", name: "no manifest for current context",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"DIFFERENT_MANIFEST": {}}, Manifests: map[string]*config.Manifest{"DIFFERENT_MANIFEST": {}},
CurrentContext: "testContext", CurrentContext: "testContext",
@ -223,11 +167,9 @@ func TestEnsureComplete(t *testing.T) {
{ {
name: "complete config", name: "complete config",
config: config.Config{ config: config.Config{
Clusters: map[string]*config.ClusterPurpose{"testCluster": {}}, EncryptionConfigs: map[string]*config.EncryptionConfig{"testEncryptionConfig": {}},
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
Manifests: map[string]*config.Manifest{"testManifest": {}}, Manifests: map[string]*config.Manifest{"testManifest": {}},
EncryptionConfigs: map[string]*config.EncryptionConfig{"testEncryptionConfig": {}},
CurrentContext: "testContext", CurrentContext: "testContext",
}, },
expectedErr: nil, expectedErr: nil,
@ -247,17 +189,13 @@ func TestCurrentContextManagementConfig(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
clusterName := "def"
clusterType := "ephemeral"
managementConfig, err := conf.CurrentContextManagementConfig() managementConfig, err := conf.CurrentContextManagementConfig()
require.Error(t, err) require.Error(t, err)
assert.Nil(t, managementConfig) assert.Nil(t, managementConfig)
conf.CurrentContext = currentContextName conf.CurrentContext = currentContextName
conf.Clusters[clusterName].ClusterTypes[clusterType].ManagementConfiguration = defaultString conf.Contexts[currentContextName].ManagementConfiguration = defaultString
conf.Contexts[currentContextName].Manifest = defaultString conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
managementConfig, err = conf.CurrentContextManagementConfig() managementConfig, err = conf.CurrentContextManagementConfig()
require.NoError(t, err) require.NoError(t, err)
@ -308,42 +246,12 @@ func TestSetKubeConfigPath(t *testing.T) {
assert.Equal(t, testPath, conf.KubeConfigPath()) assert.Equal(t, testPath, conf.KubeConfigPath())
} }
func TestModifyCluster(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
co := testutil.DummyClusterOptions()
cluster, err := conf.AddCluster(co)
require.NoError(t, err)
co.Server += stringDelta
co.InsecureSkipTLSVerify = true
co.EmbedCAData = true
mcluster, err := conf.ModifyCluster(cluster, co)
require.NoError(t, err)
assert.EqualValues(t, conf.Clusters[co.Name].ClusterTypes[co.ClusterType].KubeCluster().Server, co.Server)
assert.EqualValues(t, conf.Clusters[co.Name].ClusterTypes[co.ClusterType], mcluster)
// Error case
co.CertificateAuthority = "unknown"
_, err = conf.ModifyCluster(cluster, co)
assert.Error(t, err)
}
func TestGetClusters(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
clusters := conf.GetClusters()
assert.Len(t, clusters, 6)
}
func TestGetContexts(t *testing.T) { func TestGetContexts(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
contexts := conf.GetContexts() contexts := conf.GetContexts()
assert.Len(t, contexts, 3) assert.Len(t, contexts, 4)
} }
func TestGetContext(t *testing.T) { func TestGetContext(t *testing.T) {
@ -355,7 +263,6 @@ func TestGetContext(t *testing.T) {
// Test Positives // Test Positives
assert.EqualValues(t, context.NameInKubeconf, "def_ephemeral") assert.EqualValues(t, context.NameInKubeconf, "def_ephemeral")
assert.EqualValues(t, context.KubeContext().Cluster, "def_ephemeral")
// Test Wrong Cluster // Test Wrong Cluster
_, err = conf.GetContext("unknown") _, err = conf.GetContext("unknown")
@ -378,14 +285,8 @@ func TestModifyContext(t *testing.T) {
co := testutil.DummyContextOptions() co := testutil.DummyContextOptions()
context := conf.AddContext(co) context := conf.AddContext(co)
co.Namespace += stringDelta
co.Cluster += stringDelta
co.AuthInfo += stringDelta
co.Manifest += stringDelta co.Manifest += stringDelta
conf.ModifyContext(context, co) conf.ModifyContext(context, co)
assert.EqualValues(t, conf.Contexts[co.Name].KubeContext().Namespace, co.Namespace)
assert.EqualValues(t, conf.Contexts[co.Name].KubeContext().Cluster, co.Cluster)
assert.EqualValues(t, conf.Contexts[co.Name].KubeContext().AuthInfo, co.AuthInfo)
assert.EqualValues(t, conf.Contexts[co.Name].Manifest, co.Manifest) assert.EqualValues(t, conf.Contexts[co.Name].Manifest, co.Manifest)
assert.EqualValues(t, conf.Contexts[co.Name], context) assert.EqualValues(t, conf.Contexts[co.Name], context)
} }
@ -408,55 +309,16 @@ func TestGetCurrentContext(t *testing.T) {
}) })
} }
func TestCurrentContextCluster(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
clusterName := "def"
clusterType := "ephemeral"
cluster, err := conf.CurrentContextCluster()
require.Error(t, err)
assert.Nil(t, cluster)
conf.CurrentContext = currentContextName
conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
cluster, err = conf.CurrentContextCluster()
require.NoError(t, err)
assert.Equal(t, conf.Clusters[clusterName].ClusterTypes[clusterType], cluster)
}
func TestCurrentContextAuthInfo(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
authInfo, err := conf.CurrentContextAuthInfo()
require.Error(t, err)
assert.Nil(t, authInfo)
conf.CurrentContext = currentContextName
conf.Contexts[currentContextName].Manifest = defaultString
authInfo, err = conf.CurrentContextAuthInfo()
require.NoError(t, err)
assert.Equal(t, conf.AuthInfos["k-admin"], authInfo)
}
func TestCurrentContextManifest(t *testing.T) { func TestCurrentContextManifest(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
clusterName := "def"
manifest, err := conf.CurrentContextManifest() manifest, err := conf.CurrentContextManifest()
require.Error(t, err) require.Error(t, err)
assert.Nil(t, manifest) assert.Nil(t, manifest)
conf.CurrentContext = currentContextName conf.CurrentContext = currentContextName
conf.Contexts[currentContextName].Manifest = defaultString conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
manifest, err = conf.CurrentContextManifest() manifest, err = conf.CurrentContextManifest()
require.NoError(t, err) require.NoError(t, err)
@ -467,15 +329,12 @@ func TestCurrentTargetPath(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
clusterName := "def"
manifest, err := conf.CurrentContextManifest() manifest, err := conf.CurrentContextManifest()
require.Error(t, err) require.Error(t, err)
assert.Nil(t, manifest) assert.Nil(t, manifest)
conf.CurrentContext = currentContextName conf.CurrentContext = currentContextName
conf.Contexts[currentContextName].Manifest = defaultString conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
targetPath, err := conf.CurrentContextTargetPath() targetPath, err := conf.CurrentContextTargetPath()
require.NoError(t, err) require.NoError(t, err)
@ -486,15 +345,12 @@ func TestCurrentContextEntryPoint(t *testing.T) {
conf, cleanup := testutil.InitConfig(t) conf, cleanup := testutil.InitConfig(t)
defer cleanup(t) defer cleanup(t)
clusterName := "def"
entryPoint, err := conf.CurrentContextEntryPoint(defaultString) entryPoint, err := conf.CurrentContextEntryPoint(defaultString)
require.Error(t, err) require.Error(t, err)
assert.Equal(t, "", entryPoint) assert.Equal(t, "", entryPoint)
conf.CurrentContext = currentContextName conf.CurrentContext = currentContextName
conf.Contexts[currentContextName].Manifest = defaultString conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
entryPoint, err = conf.CurrentContextEntryPoint(defaultString) entryPoint, err = conf.CurrentContextEntryPoint(defaultString)
assert.Equal(t, config.ErrMissingPhaseDocument{PhaseName: defaultString}, err) assert.Equal(t, config.ErrMissingPhaseDocument{PhaseName: defaultString}, err)
@ -661,144 +517,6 @@ func TestNewClusterComplexNameFromKubeClusterName(t *testing.T) {
} }
} }
func TestImport(t *testing.T) {
conf, cleanupConfig := testutil.InitConfig(t)
defer cleanupConfig(t)
kubeDir, cleanupKubeConfig := testutil.TempDir(t, "airship-import-tests")
defer cleanupKubeConfig(t)
kubeConfigPath := filepath.Join(kubeDir, "config")
//nolint: lll
kubeConfigContent := `
apiVersion: v1
clusters:
- cluster:
server: https://1.2.3.4:9000
name: cluster_target
- cluster:
server: https://1.2.3.4:9001
name: dummycluster_ephemeral
- cluster:
server: https://1.2.3.4:9002
name: def_target
- cluster:
server: https://1.2.3.4:9003
name: noncomplex
contexts:
- context:
cluster: cluster_target
user: cluster-admin
name: cluster-admin@cluster
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: def_target
- context:
cluster: noncomplex
user: kubernetes-admin
name: noncomplex
current-context: dummy_cluster
kind: Config
preferences: {}
users:
- name: cluster-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
- name: def-user
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
`
err := ioutil.WriteFile(kubeConfigPath, []byte(kubeConfigContent), 0600)
require.NoError(t, err)
err = conf.ImportFromKubeConfig(kubeConfigPath)
require.NoError(t, err)
t.Run("importClusters", func(t *testing.T) {
// Verify that only 3 clusters have been added (original 5 plus 3 new clusters)
// This is important since the above kubeconfig actually has 4
// clusters, but one was already defined in the airship config
assert.Len(t, conf.Clusters, 6+3)
// verify that the new clusters have been added to the config
_, err := conf.GetCluster("cluster", config.Target)
assert.NoError(t, err)
_, err = conf.GetCluster("dummycluster", config.Ephemeral)
assert.NoError(t, err)
// verify that the "noncomplex" cluster was added as a target cluster
_, err = conf.GetCluster("noncomplex", config.Target)
assert.NoError(t, err)
})
t.Run("importContexts", func(t *testing.T) {
// Verify that only 3 contexts have been added (original 3 plus 3 new contexts)
// This is important since the above kubeconfig actually has 4
// contexts, but one was already defined in the airship config
assert.Len(t, conf.Contexts, 3+3)
// verify that the new contexts have been added to the config
_, err := conf.GetContext("cluster-admin@cluster")
assert.NoError(t, err)
_, err = conf.GetContext("dummy_cluster")
assert.NoError(t, err)
// verify that the "noncomplex" context refers to the proper target "noncomplex" cluster
noncomplex, err := conf.GetContext("noncomplex")
require.NoError(t, err)
assert.Equal(t, "noncomplex_target", noncomplex.NameInKubeconf)
})
t.Run("importAuthInfos", func(t *testing.T) {
// Verify that only 2 users have been added (original 3 plus 2 new users)
// This is important since the above kubeconfig actually has 3
// users, but one was already defined in the airship config
assert.Len(t, conf.AuthInfos, 3+2)
// verify that the new users have been added to the config
_, err := conf.GetAuthInfo("cluster-admin")
assert.NoError(t, err)
_, err = conf.GetAuthInfo("kubernetes-admin")
assert.NoError(t, err)
})
}
func TestImportErrors(t *testing.T) {
conf, cleanupConfig := testutil.InitConfig(t)
defer cleanupConfig(t)
t.Run("nonexistent kubeConfig", func(t *testing.T) {
err := conf.ImportFromKubeConfig("./non/existent/file/path")
assert.Contains(t, err.Error(), "no such file or directory")
})
t.Run("malformed kubeConfig", func(t *testing.T) {
kubeDir, cleanupKubeConfig := testutil.TempDir(t, "airship-import-tests")
defer cleanupKubeConfig(t)
kubeConfigPath := filepath.Join(kubeDir, "config")
//nolint: lll
kubeConfigContent := "malformed content"
err := ioutil.WriteFile(kubeConfigPath, []byte(kubeConfigContent), 0600)
require.NoError(t, err)
err = conf.ImportFromKubeConfig(kubeConfigPath)
assert.Contains(t, err.Error(), "json parse error")
})
}
func TestManagementConfigurationByName(t *testing.T) { func TestManagementConfigurationByName(t *testing.T) {
conf, cleanupConfig := testutil.InitConfig(t) conf, cleanupConfig := testutil.InitConfig(t)
defer cleanupConfig(t) defer cleanupConfig(t)

View File

@ -18,8 +18,8 @@ package config
import ( import (
"fmt" "fmt"
"strings"
"k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@ -37,8 +37,8 @@ type Context struct {
// +optional // +optional
EncryptionConfig string `json:"encryptionConfig,omitempty"` EncryptionConfig string `json:"encryptionConfig,omitempty"`
// KubeConfig Context Object // Management configuration which will be used for all hosts in the cluster
context *api.Context ManagementConfiguration string `json:"managementConfiguration"`
} }
func (c *Context) String() string { func (c *Context) String() string {
@ -46,12 +46,10 @@ func (c *Context) String() string {
if err != nil { if err != nil {
return "" return ""
} }
kcluster := c.KubeContext()
kyaml, err := yaml.Marshal(&kcluster)
if err != nil { if err != nil {
return string(cyaml) return string(cyaml)
} }
return fmt.Sprintf("%s\n%s", string(cyaml), string(kyaml)) return string(cyaml)
} }
// PrettyString returns cluster name in a formatted string // PrettyString returns cluster name in a formatted string
@ -60,16 +58,6 @@ func (c *Context) PrettyString() string {
return fmt.Sprintf("Context: %s\n%s\n", clusterName.Name, c) return fmt.Sprintf("Context: %s\n%s\n", clusterName.Name, c)
} }
// KubeContext returns kube context object
func (c *Context) KubeContext() *api.Context {
return c.context
}
// SetKubeContext updates kube contect with given context details
func (c *Context) SetKubeContext(kc *api.Context) {
c.context = kc
}
// ClusterType returns cluster type by extracting the type portion from // ClusterType returns cluster type by extracting the type portion from
// the complex cluster name // the complex cluster name
func (c *Context) ClusterType() string { func (c *Context) ClusterType() string {
@ -81,3 +69,48 @@ func (c *Context) ClusterType() string {
func (c *Context) ClusterName() string { func (c *Context) ClusterName() string {
return NewClusterComplexNameFromKubeClusterName(c.NameInKubeconf).Name return NewClusterComplexNameFromKubeClusterName(c.NameInKubeconf).Name
} }
// ClusterComplexName holds the complex cluster name information
// Encapsulates the different operations around using it.
type ClusterComplexName struct {
Name string
Type string
}
// NewClusterComplexName returns a ClusterComplexName with the given name and type.
func NewClusterComplexName(clusterName, clusterType string) ClusterComplexName {
return ClusterComplexName{
Name: clusterName,
Type: clusterType,
}
}
// NewClusterComplexNameFromKubeClusterName takes the name of a cluster in a
// format which might be found in a kubeconfig file. This may be a simple
// string (e.g. myCluster), or it may be prepended with the type of the cluster
// (e.g. myCluster_target)
//
// If a valid cluster type was appended, the returned ClusterComplexName will
// have that type. If no cluster type is provided, the
// AirshipDefaultClusterType will be used.
func NewClusterComplexNameFromKubeClusterName(kubeClusterName string) ClusterComplexName {
parts := strings.Split(kubeClusterName, AirshipClusterNameSeparator)
if len(parts) == 1 {
return NewClusterComplexName(kubeClusterName, AirshipDefaultClusterType)
}
// kubeClusterName matches the format myCluster_something.
// Let's check if "something" is a clusterType.
potentialType := parts[len(parts)-1]
for _, ct := range AllClusterTypes {
if potentialType == ct {
// Rejoin the parts in the case of "my_cluster_etc_etc_<clusterType>"
name := strings.Join(parts[:len(parts)-1], AirshipClusterNameSeparator)
return NewClusterComplexName(name, potentialType)
}
}
// "something" is not a valid clusterType, so just use the default
return NewClusterComplexName(kubeClusterName, AirshipDefaultClusterType)
}

View File

@ -163,12 +163,12 @@ func (e ErrMissingCurrentContext) Error() string {
// ErrMissingManagementConfiguration means the management configuration was not defined for the active cluster. // ErrMissingManagementConfiguration means the management configuration was not defined for the active cluster.
type ErrMissingManagementConfiguration struct { type ErrMissingManagementConfiguration struct {
cluster *Cluster context *Context
} }
func (e ErrMissingManagementConfiguration) Error() string { func (e ErrMissingManagementConfiguration) Error() string {
return fmt.Sprintf("Management configuration %s for cluster %s undefined.", e.cluster.ManagementConfiguration, return fmt.Sprintf("Management configuration %s for cluster %s undefined.", e.context.ManagementConfiguration,
e.cluster.NameInKubeconf) e.context.NameInKubeconf)
} }
// ErrMissingPrimaryRepo returned when Primary Repository is not set in context manifest // ErrMissingPrimaryRepo returned when Primary Repository is not set in context manifest
@ -206,14 +206,6 @@ func (e ErrConflictingClusterOptions) Error() string {
return "Specifying certificate-authority and insecure-skip-tls-verify mode is not allowed at the same time." return "Specifying certificate-authority and insecure-skip-tls-verify mode is not allowed at the same time."
} }
// ErrEmptyClusterName returned when empty cluster name is set
type ErrEmptyClusterName struct {
}
func (e ErrEmptyClusterName) Error() string {
return "Cluster name must not be empty."
}
// ErrConflictingContextOptions returned when both context and --current is set at same time // ErrConflictingContextOptions returned when both context and --current is set at same time
type ErrConflictingContextOptions struct { type ErrConflictingContextOptions struct {
} }

View File

@ -22,39 +22,15 @@ import (
"opendev.org/airship/airshipctl/pkg/errors" "opendev.org/airship/airshipctl/pkg/errors"
) )
// AuthInfoOptions holds all configurable options for
// authentication information or credential
type AuthInfoOptions struct {
Name string
ClientCertificate string
ClientKey string
Token string
Username string
Password string
EmbedCertData bool
}
// ContextOptions holds all configurable options for context // ContextOptions holds all configurable options for context
type ContextOptions struct { type ContextOptions struct {
Name string Name string
ClusterType string ClusterType string
CurrentContext bool CurrentContext bool
Cluster string Manifest string
AuthInfo string Current bool
Manifest string ManagementConfiguration string
EncryptionConfig string EncryptionConfig string
Namespace string
Current bool
}
// ClusterOptions holds all configurable options for cluster configuration
type ClusterOptions struct {
Name string
ClusterType string
Server string
InsecureSkipTLSVerify bool
CertificateAuthority string
EmbedCAData bool
} }
// ManifestOptions holds all configurable options for manifest configuration // ManifestOptions holds all configurable options for manifest configuration
@ -86,33 +62,6 @@ type EncryptionConfigOptions struct {
// is possible to create (and validate) these objects without using the command // is possible to create (and validate) these objects without using the command
// line. // line.
// Validate checks for the possible authentication values and returns
// Error when invalid value or incompatible choice of values given
func (o *AuthInfoOptions) Validate() error {
// TODO(howell): This prevents a user of airshipctl from creating a
// credential with both a bearer-token and a user/password, but it does
// not prevent a user from adding a bearer-token to a credential which
// already had a user/pass and visa-versa. This could create bugs if a
// user at first chooses one method, but later switches to another.
if o.Token != "" && (o.Username != "" || o.Password != "") {
return ErrConflictingAuthOptions{}
}
if !o.EmbedCertData {
return nil
}
if err := checkExists("client-certificate", o.ClientCertificate); err != nil {
return err
}
if err := checkExists("client-key", o.ClientKey); err != nil {
return err
}
return nil
}
// Validate checks for the possible context option values and returns // Validate checks for the possible context option values and returns
// Error when invalid value or incompatible choice of values given // Error when invalid value or incompatible choice of values given
func (o *ContextOptions) Validate() error { func (o *ContextOptions) Validate() error {
@ -129,58 +78,10 @@ func (o *ContextOptions) Validate() error {
return nil return nil
} }
// If the cluster-type was specified, verify that it's valid
if o.ClusterType != "" {
if err := ValidClusterType(o.ClusterType); err != nil {
return err
}
}
// TODO Manifest, Cluster could be validated against the existing config maps // TODO Manifest, Cluster could be validated against the existing config maps
return nil return nil
} }
// Validate checks for the possible cluster option values and returns
// Error when invalid value or incompatible choice of values given
func (o *ClusterOptions) Validate() error {
if o.Name == "" {
return ErrEmptyClusterName{}
}
err := ValidClusterType(o.ClusterType)
if err != nil {
return err
}
if o.InsecureSkipTLSVerify && o.CertificateAuthority != "" {
return ErrConflictingClusterOptions{}
}
if !o.EmbedCAData {
return nil
}
if err := checkExists("certificate-authority", o.CertificateAuthority); err != nil {
return err
}
return nil
}
func checkExists(flagName, path string) error {
if path == "" {
return ErrMissingFlag{FlagName: flagName}
}
if _, err := os.Stat(path); err != nil {
return ErrCheckFile{
FlagName: flagName,
Path: path,
InternalErr: err,
}
}
return nil
}
// Validate checks for the possible manifest option values and returns // Validate checks for the possible manifest option values and returns
// Error when invalid value or incompatible choice of values given // Error when invalid value or incompatible choice of values given
func (o *ManifestOptions) Validate() error { func (o *ManifestOptions) Validate() error {
@ -247,3 +148,17 @@ func (o EncryptionConfigOptions) backedByFileSystem() bool {
func (o EncryptionConfigOptions) backedByAPIServer() bool { func (o EncryptionConfigOptions) backedByAPIServer() bool {
return o.KeySecretName != "" || o.KeySecretNamespace != "" return o.KeySecretName != "" || o.KeySecretNamespace != ""
} }
func checkExists(flagName, path string) error {
if path == "" {
return ErrMissingFlag{FlagName: flagName}
}
if _, err := os.Stat(path); err != nil {
return ErrCheckFile{
FlagName: flagName,
Path: path,
InternalErr: err,
}
}
return nil
}

View File

@ -17,83 +17,13 @@ limitations under the License.
package config_test package config_test
import ( import (
"io/ioutil"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/config"
) )
func TestAuthInfoOptionsValidate(t *testing.T) {
aRealFile, err := ioutil.TempFile("", "a-real-file")
require.NoError(t, err)
aRealFilename := aRealFile.Name()
defer os.Remove(aRealFilename)
tests := []struct {
name string
testOptions config.AuthInfoOptions
expectError bool
}{
{
name: "TokenAndUserPass",
testOptions: config.AuthInfoOptions{
Token: "testToken",
Username: "testUser",
Password: "testPassword",
},
expectError: true,
},
{
name: "DontEmbed",
testOptions: config.AuthInfoOptions{
EmbedCertData: false,
},
expectError: false,
},
{
name: "EmbedWithoutCert",
testOptions: config.AuthInfoOptions{
EmbedCertData: true,
},
expectError: true,
},
{
name: "EmbedWithoutClientKey",
testOptions: config.AuthInfoOptions{
EmbedCertData: true,
ClientCertificate: aRealFilename,
},
expectError: true,
},
{
name: "EmbedWithCertAndClientKey",
testOptions: config.AuthInfoOptions{
EmbedCertData: true,
ClientCertificate: aRealFilename,
ClientKey: aRealFilename,
},
expectError: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(subTest *testing.T) {
err := tt.testOptions.Validate()
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestContextOptionsValidate(t *testing.T) { func TestContextOptionsValidate(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -107,14 +37,6 @@ func TestContextOptionsValidate(t *testing.T) {
}, },
expectError: true, expectError: true,
}, },
{
name: "InvalidClusterType",
testOptions: config.ContextOptions{
Name: "testContext",
ClusterType: "badType",
},
expectError: true,
},
{ {
name: "SettingCurrentContext", name: "SettingCurrentContext",
testOptions: config.ContextOptions{ testOptions: config.ContextOptions{
@ -152,93 +74,3 @@ func TestContextOptionsValidate(t *testing.T) {
}) })
} }
} }
func TestClusterOptionsValidate(t *testing.T) {
aRealfile, err := ioutil.TempFile("", "a-real-file")
require.NoError(t, err)
aRealFilename := aRealfile.Name()
defer os.Remove(aRealFilename)
tests := []struct {
name string
testOptions config.ClusterOptions
expectError bool
}{
{
name: "MissingName",
testOptions: config.ClusterOptions{
Name: "",
},
expectError: true,
},
{
name: "InvalidClusterType",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "badType",
},
expectError: true,
},
{
name: "InsecureSkipTLSVerifyAndCertificateAuthority",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "target",
InsecureSkipTLSVerify: true,
CertificateAuthority: "cert_file",
},
expectError: true,
},
{
name: "DontEmbed",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "target",
EmbedCAData: false,
},
expectError: false,
},
{
name: "EmbedWithoutCA",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "target",
EmbedCAData: true,
},
expectError: true,
},
{
name: "EmbedWithFaultyCA",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "target",
EmbedCAData: true,
CertificateAuthority: "not-a-real-file",
},
expectError: true,
},
{
name: "EmbedWithGoodCA",
testOptions: config.ClusterOptions{
Name: "testCluster",
ClusterType: "target",
EmbedCAData: true,
CertificateAuthority: aRealFilename,
},
expectError: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(subTest *testing.T) {
err := tt.testOptions.Validate()
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -1,6 +0,0 @@
LocationOfOrigin: ""
client-certificate: ZHVtbXlfY2VydGlmaWNhdGU=
client-key: ZHVtbXlfa2V5
password: ZHVtbXlfcGFzc3dvcmQ=
token: ZHVtbXlfdG9rZW4=
username: dummy_username

View File

@ -1,6 +0,0 @@
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config
LocationOfOrigin: ""
certificate-authority: dummy_ca
server: http://dummy.server

View File

@ -1,17 +1,9 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
clusters:
dummy_cluster:
clusterType:
ephemeral:
clusterKubeconf: dummy_cluster_ephemeral
managementConfiguration: dummy_management_config
target:
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config
contexts: contexts:
dummy_context: dummy_context:
contextKubeconf: dummy_cluster_ephemeral contextKubeconf: dummy_cluster_ephemeral
encryptionConfig: dummy_encryption_config encryptionConfig: dummy_encryption_config
managementConfiguration: dummy_management_config
manifest: dummy_manifest manifest: dummy_manifest
currentContext: dummy_context currentContext: dummy_context
encryptionConfigs: encryptionConfigs:
@ -43,5 +35,3 @@ manifests:
permissions: permissions:
DirectoryPermission: 488 DirectoryPermission: 488
FilePermission: 416 FilePermission: 416
users:
dummy_user: {}

View File

@ -1,8 +1,4 @@
contextKubeconf: dummy_cluster_ephemeral contextKubeconf: dummy_cluster_ephemeral
encryptionConfig: dummy_encryption_config encryptionConfig: dummy_encryption_config
managementConfiguration: dummy_management_config
manifest: dummy_manifest manifest: dummy_manifest
LocationOfOrigin: ""
cluster: dummy_cluster_ephemeral
namespace: dummy_namespace
user: dummy_user

View File

@ -1,8 +0,0 @@
Cluster: dummy_cluster
target:
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config
LocationOfOrigin: ""
certificate-authority: dummy_ca
server: http://dummy.server

View File

@ -27,15 +27,14 @@ func NewConfig() *Config {
return &Config{ return &Config{
Kind: AirshipConfigKind, Kind: AirshipConfigKind,
APIVersion: AirshipConfigAPIVersion, APIVersion: AirshipConfigAPIVersion,
Clusters: make(map[string]*ClusterPurpose),
Permissions: Permissions{ Permissions: Permissions{
DirectoryPermission: AirshipDefaultDirectoryPermission, DirectoryPermission: AirshipDefaultDirectoryPermission,
FilePermission: AirshipDefaultFilePermission, FilePermission: AirshipDefaultFilePermission,
}, },
AuthInfos: make(map[string]*AuthInfo),
Contexts: map[string]*Context{ Contexts: map[string]*Context{
AirshipDefaultContext: { AirshipDefaultContext: {
Manifest: AirshipDefaultManifest, Manifest: AirshipDefaultManifest,
ManagementConfiguration: AirshipDefaultManagementConfiguration,
}, },
}, },
CurrentContext: AirshipDefaultContext, CurrentContext: AirshipDefaultContext,
@ -87,14 +86,6 @@ func NewContext() *Context {
return &Context{} return &Context{}
} }
// NewCluster is a convenience function that returns a new Cluster
func NewCluster() *Cluster {
return &Cluster{
NameInKubeconf: "",
ManagementConfiguration: AirshipDefaultManagementConfiguration,
}
}
// NewManifest is a convenience function that returns a new Manifest // NewManifest is a convenience function that returns a new Manifest
// object with non-nil maps // object with non-nil maps
func NewManifest() *Manifest { func NewManifest() *Manifest {
@ -114,11 +105,6 @@ func NewRepository() *Repository {
} }
} }
// NewAuthInfo is a convenience function that returns a new AuthInfo
func NewAuthInfo() *AuthInfo {
return &AuthInfo{}
}
// EncodeString returns the base64 encoding of given string // EncodeString returns the base64 encoding of given string
func EncodeString(given string) string { func EncodeString(given string) string {
return base64.StdEncoding.EncodeToString([]byte(given)) return base64.StdEncoding.EncodeToString([]byte(given))

View File

@ -60,7 +60,6 @@ func TestDeploy(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expectedErrorString string expectedErrorString string
clusterPurposes map[string]*config.ClusterPurpose
phaseName string phaseName string
events []applyevent.Event events []applyevent.Event
}{ }{
@ -95,9 +94,6 @@ func TestDeploy(t *testing.T) {
ao.Applier = cliApplier ao.Applier = cliApplier
ao.EventChannel = ch ao.EventChannel = ch
} }
if tt.clusterPurposes != nil {
rs.Clusters = tt.clusterPurposes
}
if tt.phaseName != "" { if tt.phaseName != "" {
ao.PhaseName = tt.phaseName ao.PhaseName = tt.phaseName
} }

View File

@ -1,25 +1,7 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
container:
volume: /tmp/airship:/config
image: quay.io/airshipit/isogen:latest-ubuntu_focal
containerRuntime: docker
builder:
userDataFileName: user-data
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
remoteDirect:
isoUrl: http://localhost:8099/ubuntu-focal.iso
clusters:
dummycluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummycluster_ephemeral
contexts: contexts:
dummy_cluster: dummy_cluster:
contextKubeconf: dummy_cluster contextKubeconf: dummycluster_ephemeral
manifest: dummy_manifest manifest: dummy_manifest
currentContext: dummy_cluster currentContext: dummy_cluster
kind: Config kind: Config
@ -39,5 +21,3 @@ manifests:
url: http://dummy.url.com/primary.git url: http://dummy.url.com/primary.git
subPath: primary/site/test-site subPath: primary/site/test-site
targetPath: testdata targetPath: testdata
users:
dummy_user: {}

View File

@ -1,26 +1,7 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
container:
volume: /tmp/airship:/config
image: quay.io/airshipit/isogen:latest-ubuntu_focal
containerRuntime: docker
builder:
userDataFileName: user-data
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
remoteDirect:
isoUrl: http://localhost:8099/ubuntu-focal.iso
remoteType: redfish
clusters:
dummycluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummycluster_ephemeral
contexts: contexts:
dummy_cluster: dummy_cluster:
contextKubeconf: dummy_cluster contextKubeconf: dummycluster_ephemeral
manifest: dummy_manifest manifest: dummy_manifest
currentContext: dummy_cluster currentContext: dummy_cluster
kind: Config kind: Config
@ -41,5 +22,3 @@ manifests:
tag: v1.0.1 tag: v1.0.1
url: http://dummy.url.com/primary.git url: http://dummy.url.com/primary.git
subPath: valid_site subPath: valid_site
users:
dummy_user: {}

View File

@ -1,24 +1,4 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
default:
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-ubuntu_focal
volume: /srv/iso:/config
remoteDirect:
isoUrl: http://localhost:8099/ubuntu-focal.iso
clusters:
default:
clusterType:
target:
bootstrapInfo: ""
clusterKubeconf: default_target
kubernetes:
clusterType: {}
contexts: contexts:
default: default:
contextKubeconf: default_target contextKubeconf: default_target
@ -36,5 +16,3 @@ manifests:
tag: "" tag: ""
url: https://opendev.org/airship/treasuremap url: https://opendev.org/airship/treasuremap
targetPath: /tmp/default targetPath: /tmp/default
users:
admin: {}

View File

@ -36,12 +36,6 @@ func DummyConfig() *config.Config {
conf := &config.Config{ conf := &config.Config{
Kind: config.AirshipConfigKind, Kind: config.AirshipConfigKind,
APIVersion: config.AirshipConfigAPIVersion, APIVersion: config.AirshipConfigAPIVersion,
Clusters: map[string]*config.ClusterPurpose{
"dummy_cluster": DummyClusterPurpose(),
},
AuthInfos: map[string]*config.AuthInfo{
"dummy_user": DummyAuthInfo(),
},
Permissions: config.Permissions{ Permissions: config.Permissions{
DirectoryPermission: config.AirshipDefaultDirectoryPermission, DirectoryPermission: config.AirshipDefaultDirectoryPermission,
FilePermission: config.AirshipDefaultFilePermission, FilePermission: config.AirshipDefaultFilePermission,
@ -61,10 +55,6 @@ func DummyConfig() *config.Config {
CurrentContext: "dummy_context", CurrentContext: "dummy_context",
} }
conf.SetKubeConfig(kubeconfig.NewConfig()) conf.SetKubeConfig(kubeconfig.NewConfig())
dummyCluster := conf.Clusters["dummy_cluster"]
conf.KubeConfig().Clusters["dummy_cluster_target"] = dummyCluster.ClusterTypes[config.Target].KubeCluster()
conf.KubeConfig().Clusters["dummy_cluster_ephemeral"] = dummyCluster.ClusterTypes[config.Ephemeral].KubeCluster()
return conf return conf
} }
@ -73,26 +63,7 @@ func DummyContext() *config.Context {
c := config.NewContext() c := config.NewContext()
c.NameInKubeconf = "dummy_cluster_ephemeral" c.NameInKubeconf = "dummy_cluster_ephemeral"
c.Manifest = "dummy_manifest" c.Manifest = "dummy_manifest"
context := kubeconfig.NewContext()
context.Namespace = "dummy_namespace"
context.AuthInfo = "dummy_user"
context.Cluster = "dummy_cluster_ephemeral"
c.EncryptionConfig = "dummy_encryption_config" c.EncryptionConfig = "dummy_encryption_config"
c.SetKubeContext(context)
return c
}
// DummyCluster creates a Cluster config object for unit testing
func DummyCluster() *config.Cluster {
c := config.NewCluster()
cluster := kubeconfig.NewCluster()
cluster.Server = "http://dummy.server"
cluster.InsecureSkipTLSVerify = false
cluster.CertificateAuthority = "dummy_ca"
c.SetKubeCluster(cluster)
c.NameInKubeconf = "dummy_cluster_target"
c.ManagementConfiguration = "dummy_management_config" c.ManagementConfiguration = "dummy_management_config"
return c return c
} }
@ -140,29 +111,6 @@ func DummyRepoCheckout() *config.RepoCheckout {
} }
} }
// DummyAuthInfo creates a AuthInfo config object for unit testing
func DummyAuthInfo() *config.AuthInfo {
a := config.NewAuthInfo()
authinfo := kubeconfig.NewAuthInfo()
authinfo.Username = "dummy_username"
authinfo.Password = "dummy_password"
authinfo.ClientCertificate = "dummy_certificate"
authinfo.ClientKey = "dummy_key"
authinfo.Token = "dummy_token"
encodedAuthInfo := config.EncodeAuthInfo(authinfo)
a.SetKubeAuthInfo(encodedAuthInfo)
return a
}
// DummyClusterPurpose creates ClusterPurpose config object for unit testing
func DummyClusterPurpose() *config.ClusterPurpose {
cp := config.NewClusterPurpose()
cp.ClusterTypes["ephemeral"] = DummyCluster()
cp.ClusterTypes["ephemeral"].NameInKubeconf = "dummy_cluster_ephemeral"
cp.ClusterTypes["target"] = DummyCluster()
return cp
}
// InitConfig creates a Config object meant for testing. // InitConfig creates a Config object meant for testing.
// //
// The returned config object will be associated with real files stored in a // The returned config object will be associated with real files stored in a
@ -188,46 +136,17 @@ func InitConfig(t *testing.T) (conf *config.Config, cleanup func(*testing.T)) {
return conf, cleanup return conf, cleanup
} }
// DummyClusterOptions creates ClusterOptions config object
// for unit testing
func DummyClusterOptions() *config.ClusterOptions {
co := &config.ClusterOptions{}
co.Name = "dummy_cluster"
co.ClusterType = config.Ephemeral
co.Server = "http://1.1.1.1"
co.InsecureSkipTLSVerify = false
co.CertificateAuthority = ""
co.EmbedCAData = false
return co
}
// DummyContextOptions creates ContextOptions config object // DummyContextOptions creates ContextOptions config object
// for unit testing // for unit testing
func DummyContextOptions() *config.ContextOptions { func DummyContextOptions() *config.ContextOptions {
co := &config.ContextOptions{} co := &config.ContextOptions{}
co.Name = "dummy_context" co.Name = "dummy_context"
co.Manifest = "dummy_manifest" co.Manifest = "dummy_manifest"
co.AuthInfo = "dummy_user"
co.CurrentContext = false co.CurrentContext = false
co.Namespace = "dummy_namespace"
co.EncryptionConfig = "dummy_encryption_config" co.EncryptionConfig = "dummy_encryption_config"
return co return co
} }
// DummyAuthInfoOptions creates AuthInfoOptions config object
// for unit testing
func DummyAuthInfoOptions() *config.AuthInfoOptions {
authinfo := &config.AuthInfoOptions{}
authinfo.Username = "dummy_username"
authinfo.Password = "dummy_password"
authinfo.ClientCertificate = "dummy_certificate"
authinfo.ClientKey = "dummy_key"
authinfo.Token = "dummy_token"
return authinfo
}
// DummyEncryptionConfig creates EncryptionConfigOptions object // DummyEncryptionConfig creates EncryptionConfigOptions object
// for unit testing // for unit testing
func DummyEncryptionConfig() *config.EncryptionConfig { func DummyEncryptionConfig() *config.EncryptionConfig {
@ -275,31 +194,6 @@ func DummyManifestOptions() *config.ManifestOptions {
const ( const (
testConfigYAML = `apiVersion: airshipit.org/v1alpha1 testConfigYAML = `apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
default: {}
clusters:
straggler:
clusterType:
ephemeral:
clusterKubeconf: notThere
def:
clusterType:
ephemeral:
clusterKubeconf: def_ephemeral
target:
clusterKubeconf: def_target
onlyinkubeconf:
clusterType:
target:
clusterKubeconf: onlyinkubeconf_target
wrongonlyinconfig:
clusterType: {}
wrongonlyinkubeconf:
clusterType:
target:
clusterKubeconf: wrongonlyinkubeconf_target
clustertypenil:
clusterType: null
contexts: contexts:
def_ephemeral: def_ephemeral:
contextKubeconf: def_ephemeral contextKubeconf: def_ephemeral
@ -310,11 +204,7 @@ contexts:
encryptionConfigs: {} encryptionConfigs: {}
currentContext: "" currentContext: ""
kind: Config kind: Config
manifests: {} manifests: {}`
users:
k-admin: {}
k-other: {}
def-user: {}`
//nolint:lll //nolint:lll
testKubeConfigYAML = `apiVersion: v1 testKubeConfigYAML = `apiVersion: v1

View File

@ -53,4 +53,3 @@ mkdir -p $HOME/.airship
echo "Generate ~/.airship/config and ~/.airship/kubeconfig" echo "Generate ~/.airship/config and ~/.airship/kubeconfig"
envsubst <"${AIRSHIPCTL_WS}/tools/deployment/templates/airshipconfig_template" > ~/.airship/config envsubst <"${AIRSHIPCTL_WS}/tools/deployment/templates/airshipconfig_template" > ~/.airship/config
envsubst <"${AIRSHIPCTL_WS}/tools/deployment/templates/kubeconfig_template" > ~/.airship/kubeconfig envsubst <"${AIRSHIPCTL_WS}/tools/deployment/templates/kubeconfig_template" > ~/.airship/kubeconfig

View File

@ -1,19 +1,4 @@
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
container:
volume: ${AIRSHIP_CONFIG_ISO_GEN_TARGET_PATH}:/config
image: ${AIRSHIP_CONFIG_ISO_BUILDER_DOCKER_IMAGE}
containerRuntime: docker
remoteDirect:
remoteType: ${REMOTE_TYPE}
insecure: ${REMOTE_INSECURE}
useproxy: ${REMOTE_PROXY}
isoUrl: http://${AIRSHIP_CONFIG_ISO_SERVE_HOST}:${AIRSHIP_CONFIG_ISO_PORT}/${AIRSHIP_CONFIG_ISO_NAME}
builder:
userDataFileName: user-data
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
managementConfiguration: managementConfiguration:
dummy_management_config: dummy_management_config:
type: ${REMOTE_TYPE} type: ${REMOTE_TYPE}
@ -21,26 +6,15 @@ managementConfiguration:
useproxy: ${REMOTE_PROXY} useproxy: ${REMOTE_PROXY}
systemActionRetries: ${SYSTEM_ACTION_RETRIES} systemActionRetries: ${SYSTEM_ACTION_RETRIES}
systemRebootDelay: ${SYSTEM_REBOOT_DELAY} systemRebootDelay: ${SYSTEM_REBOOT_DELAY}
clusters:
ephemeral-cluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: ephemeral-cluster_ephemeral
managementConfiguration: dummy_management_config
target-cluster:
clusterType:
target:
clusterKubeconf: target-cluster_target
managementConfiguration: dummy_management_config
contexts: contexts:
ephemeral-cluster: ephemeral-cluster:
contextKubeconf: ephemeral-context contextKubeconf: ephemeral-cluster_ephemeral
manifest: dummy_manifest manifest: dummy_manifest
managementConfiguration: dummy_management_config
target-cluster: target-cluster:
contextKubeconf: target-context contextKubeconf: target-cluster_target
manifest: dummy_manifest manifest: dummy_manifest
managementConfiguration: dummy_management_config
currentContext: ephemeral-cluster currentContext: ephemeral-cluster
kind: Config kind: Config
manifests: manifests:
@ -57,6 +31,3 @@ manifests:
metadataPath: manifests/metadata.yaml metadataPath: manifests/metadata.yaml
subPath: ${AIRSHIP_SITE_NAME} subPath: ${AIRSHIP_SITE_NAME}
targetPath: ${AIRSHIP_CONFIG_MANIFEST_DIRECTORY} targetPath: ${AIRSHIP_CONFIG_MANIFEST_DIRECTORY}
users:
ephemeral-cluster-admin: {}
target-cluster-admin: {}

View File

@ -54,17 +54,11 @@ function generate_airshipconf {
cat <<EOL > ${AIRSHIPCONFIG} cat <<EOL > ${AIRSHIPCONFIG}
apiVersion: airshipit.org/v1alpha1 apiVersion: airshipit.org/v1alpha1
clusters:
${CONTEXT}_${cluster}:
clusterType:
${cluster}:
bootstrapInfo: default
clusterKubeconf: ${CONTEXT}_${cluster}
managementConfiguration: default
contexts: contexts:
${CONTEXT}_${cluster}: ${CONTEXT}_${cluster}:
contextKubeconf: ${CONTEXT}_${cluster} contextKubeconf: ${CONTEXT}_${cluster}
manifest: ${CONTEXT}_${cluster} manifest: ${CONTEXT}_${cluster}
managementConfiguration: default
currentContext: ${CONTEXT}_${cluster} currentContext: ${CONTEXT}_${cluster}
kind: Config kind: Config
managementConfiguration: managementConfiguration:
@ -86,8 +80,6 @@ manifests:
url: https://opendev.org/airship/treasuremap url: https://opendev.org/airship/treasuremap
subPath: ${SITE_ROOT}/${SITE} subPath: ${SITE_ROOT}/${SITE}
targetPath: ${MANIFEST_ROOT} targetPath: ${MANIFEST_ROOT}
users:
${CONTEXT}_${cluster}: {}
EOL EOL
} }