Mount kubeconfig to GenericContainer executor

Change-Id: Ide647fc0cfd9d281d57eeeaf8b3f9c33f59e7fdf
This commit is contained in:
Vladislav Kuzmin 2021-02-11 16:47:26 +04:00
parent e2af947337
commit 63e6012133
5 changed files with 121 additions and 0 deletions

View File

@ -27,6 +27,14 @@ const (
GenericContainerTypeAirship GenericContainerType = "airship"
// GenericContainerTypeKrm specifies that kustomize krm function will be used
GenericContainerTypeKrm GenericContainerType = "krm"
// KubeConfigEnvKey uses as a key for kubeconfig env variable
KubeConfigEnvKey = "KUBECONFIG"
// KubeConfigPath is a path for mounted kubeconfig inside container
KubeConfigPath = "/kubeconfig"
// KubeConfigEnvKeyContext uses as a key for kubectl context env variable
KubeConfigEnvKeyContext = "KCTL_CONTEXT"
// KubeConfigEnv uses as a kubeconfig env variable
KubeConfigEnv = KubeConfigEnvKey + "=" + KubeConfigPath
)
// +kubebuilder:object:root=true

View File

@ -0,0 +1,9 @@
---
apiVersion: airshipit.org/v1alpha1
kind: ClusterMap
metadata:
labels:
airshipit.org/deploy-k8s: "false"
name: main-map
map:
testCluster: {}

View File

@ -1,2 +1,3 @@
resources:
- secret.yaml
- cluster-map.yaml

View File

@ -25,6 +25,7 @@ import (
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/errors"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/log"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
@ -40,6 +41,7 @@ type ContainerExecutor struct {
ExecutorBundle document.Bundle
ExecutorDocument document.Document
Helper ifc.Helper
Options ifc.ExecutorConfig
}
// NewContainerExecutor creates instance of phase executor
@ -70,6 +72,7 @@ func NewContainerExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
ClientFunc: container.NewClientV1Alpha1,
Helper: cfg.Helper,
Container: apiObj,
Options: cfg,
}, nil
}
@ -82,6 +85,14 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
Message: "starting generic container",
})
if c.Options.ClusterName != "" {
cleanup, err := c.SetKubeConfig()
if err != nil {
handleError(evtCh, err)
}
defer cleanup()
}
input, err := bundleReader(c.ExecutorBundle)
if err != nil {
// TODO move bundleFactory here, and make sure that if executorDoc is not defined, we dont fail
@ -121,6 +132,31 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
})
}
// SetKubeConfig adds env variable and mounts kubeconfig to container
func (c *ContainerExecutor) SetKubeConfig() (kubeconfig.Cleanup, error) {
clusterMap, err := c.Helper.ClusterMap()
if err != nil {
return nil, err
}
context, err := clusterMap.ClusterKubeconfigContext(c.Options.ClusterName)
if err != nil {
return nil, err
}
kubeConfigSrc, cleanup, err := c.Options.KubeConfig.GetFile()
if err != nil {
return nil, err
}
c.Container.Spec.StorageMounts = append(c.Container.Spec.StorageMounts, v1alpha1.StorageMount{
MountType: "bind",
Src: kubeConfigSrc,
DstPath: v1alpha1.KubeConfigPath,
})
envs := []string{v1alpha1.KubeConfigEnv, v1alpha1.KubeConfigEnvKeyContext + "=" + context}
c.Container.Spec.EnvVars = append(c.Container.Spec.EnvVars, envs...)
return cleanup, nil
}
// bundleReader sets input for function
func bundleReader(bundle document.Bundle) (io.Reader, error) {
buf := &bytes.Buffer{}

View File

@ -14,6 +14,7 @@ package executors_test
import (
"fmt"
"io"
"testing"
"github.com/stretchr/testify/assert"
@ -24,6 +25,7 @@ import (
"opendev.org/airship/airshipctl/pkg/container"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
@ -174,6 +176,14 @@ type: Opaque
Container: tt.containerAPI,
ClientFunc: tt.clientFunc,
Helper: makeDefaultHelper(t, tt.targetPath, "../metadata.yaml"),
Options: ifc.ExecutorConfig{
ClusterName: "testCluster",
KubeConfig: fakeKubeConfig{
getFile: func() (string, kubeconfig.Cleanup, error) {
return "testPath", func() {}, nil
},
},
},
}
ch := make(chan events.Event)
@ -199,3 +209,60 @@ type: Opaque
})
}
}
func TestSetKubeConfig(t *testing.T) {
getFileErr := fmt.Errorf("failed to get file")
testCases := []struct {
name string
opts ifc.ExecutorConfig
expectedErr error
}{
{
name: "Set valid kubeconfig",
opts: ifc.ExecutorConfig{
ClusterName: "testCluster",
KubeConfig: fakeKubeConfig{
getFile: func() (string, kubeconfig.Cleanup, error) {
return "testPath", func() {}, nil
},
},
},
},
{
name: "Failed to get kubeconfig file",
opts: ifc.ExecutorConfig{
ClusterName: "testCluster",
KubeConfig: fakeKubeConfig{
getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, getFileErr
},
},
},
expectedErr: getFileErr,
},
}
for _, tc := range testCases {
tt := tc
t.Run(tt.name, func(t *testing.T) {
e := executors.ContainerExecutor{
Options: tt.opts,
Container: &v1alpha1.GenericContainer{},
Helper: makeDefaultHelper(t, singleExecutorBundlePath, "../metadata.yaml"),
}
_, err := e.SetKubeConfig()
assert.Equal(t, tt.expectedErr, err)
})
}
}
type fakeKubeConfig struct {
getFile func() (string, kubeconfig.Cleanup, error)
}
func (k fakeKubeConfig) GetFile() (string, kubeconfig.Cleanup, error) { return k.getFile() }
func (k fakeKubeConfig) Write(_ io.Writer) error { return nil }
func (k fakeKubeConfig) WriteFile(path string) error { return nil }
func (k fakeKubeConfig) WriteTempFile(dumpRoot string) (string, kubeconfig.Cleanup, error) {
return k.getFile()
}