diff --git a/pkg/api/v1alpha1/groupversion_info.go b/pkg/api/v1alpha1/groupversion_info.go index 15b902d47..c11f9ac35 100644 --- a/pkg/api/v1alpha1/groupversion_info.go +++ b/pkg/api/v1alpha1/groupversion_info.go @@ -44,6 +44,7 @@ func init() { &Clusterctl{}, &Phase{}, &PhasePlan{}, + &KubeConfig{}, ) _ = AddToScheme(Scheme) //nolint:errcheck } diff --git a/pkg/api/v1alpha1/kubeconfig_types.go b/pkg/api/v1alpha1/kubeconfig_types.go new file mode 100644 index 000000000..12b4d2974 --- /dev/null +++ b/pkg/api/v1alpha1/kubeconfig_types.go @@ -0,0 +1,29 @@ +/* + 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api/v1" +) + +// +kubebuilder:object:root=true + +// KubeConfig object for k8s client +type KubeConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Config clientcmdapi.Config `json:"config,omitempty"` +} diff --git a/pkg/api/v1alpha1/zz_generated.deepcopy.go b/pkg/api/v1alpha1/zz_generated.deepcopy.go index aed7daeb1..20d9997a2 100644 --- a/pkg/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/v1alpha1/zz_generated.deepcopy.go @@ -85,6 +85,32 @@ func (in *InitOptions) DeepCopy() *InitOptions { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfig) DeepCopyInto(out *KubeConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Config.DeepCopyInto(&out.Config) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfig. +func (in *KubeConfig) DeepCopy() *KubeConfig { + if in == nil { + return nil + } + out := new(KubeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MoveOptions) DeepCopyInto(out *MoveOptions) { *out = *in diff --git a/pkg/document/filesystem.go b/pkg/document/filesystem.go index e059b7abe..eb51bd6a9 100644 --- a/pkg/document/filesystem.go +++ b/pkg/document/filesystem.go @@ -30,6 +30,7 @@ type File interface { type FileSystem interface { fs.FileSystem TempFile(string, string) (File, error) + TempDir(string, string) (string, error) } // Fs is adaptor to TempFile @@ -46,3 +47,8 @@ func NewDocumentFs() FileSystem { func (dfs Fs) TempFile(tmpDir string, prefix string) (File, error) { return ioutil.TempFile(tmpDir, prefix) } + +// TempDir creates a temporary directory in given root directory +func (dfs Fs) TempDir(rootDir string, prefix string) (string, error) { + return ioutil.TempDir(rootDir, prefix) +} diff --git a/pkg/k8s/utils/utils.go b/pkg/k8s/utils/utils.go index 37ab539fd..d2737f7b5 100644 --- a/pkg/k8s/utils/utils.go +++ b/pkg/k8s/utils/utils.go @@ -25,6 +25,9 @@ import ( cmdutil "k8s.io/kubectl/pkg/cmd/util" "sigs.k8s.io/cli-utils/pkg/manifestreader" + "sigs.k8s.io/yaml" + + airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1" "opendev.org/airship/airshipctl/pkg/document" ) @@ -96,3 +99,27 @@ func (mbr *ManifestBundleReader) Read() ([]*resource.Info, error) { } return mbr.StreamReader.Read() } + +// DumpKubeConfig to temporary directory +func DumpKubeConfig(kconf *airshipv1.KubeConfig, root string, fs document.FileSystem) (string, error) { + data, err := yaml.Marshal(kconf.Config) + if err != nil { + return "", err + } + + dir, err := fs.TempDir(root, "") + if err != nil { + return "", err + } + + file, err := fs.TempFile(dir, "") + if err != nil { + return "", err + } + + _, err = file.Write(data) + if err != nil { + return "", err + } + return file.Name(), nil +} diff --git a/pkg/k8s/utils/utils_test.go b/pkg/k8s/utils/utils_test.go index b0fbf164a..49e5fde5e 100644 --- a/pkg/k8s/utils/utils_test.go +++ b/pkg/k8s/utils/utils_test.go @@ -15,6 +15,7 @@ package utils import ( + "errors" "fmt" "io" "testing" @@ -23,7 +24,12 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime/schema" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api/v1" + + airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1" "opendev.org/airship/airshipctl/pkg/document" + "opendev.org/airship/airshipctl/testutil/fs" ) func TestDefaultManifestFactory(t *testing.T) { @@ -89,6 +95,87 @@ func TestManifestBundleReader(t *testing.T) { } } +func TestDumpKubeConfig(t *testing.T) { + errTmpDir := errors.New("TmpDir error") + errTmpFile := errors.New("TmpFile error") + errWriteFile := errors.New("WriteFile error") + + sampleKubeConfig := &airshipv1.KubeConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "airshipit.org/v1alpha1", + Kind: "KubeConfig", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "somename", + }, + Config: clientcmdapi.Config{ + APIVersion: "v1", + Kind: "Config", + }, + } + + testCases := []struct { + name string + fs document.FileSystem + expectedErr error + }{ + { + name: "Error temporary dir", + fs: fs.MockFileSystem{ + MockTempDir: func() (string, error) { + return "", errTmpDir + }, + }, + expectedErr: errTmpDir, + }, + { + name: "Error temporary file", + fs: fs.MockFileSystem{ + MockTempDir: func() (string, error) { return "someDir", nil }, + MockTempFile: func() (document.File, error) { return nil, errTmpFile }, + }, + expectedErr: errTmpFile, + }, + { + name: "Error write file", + fs: fs.MockFileSystem{ + MockTempDir: func() (string, error) { return "someDir", nil }, + MockTempFile: func() (document.File, error) { + return fs.TestFile{ + MockName: func() string { return "filename" }, + MockWrite: func() (int, error) { return 0, errWriteFile }, + MockClose: func() error { return nil }, + }, nil + }, + MockRemoveAll: func() error { return nil }, + }, + expectedErr: errWriteFile, + }, + { + name: "Dump without errors", + fs: fs.MockFileSystem{ + MockTempDir: func() (string, error) { return "someDir", nil }, + MockTempFile: func() (document.File, error) { + return fs.TestFile{ + MockName: func() string { return "filename" }, + MockWrite: func() (int, error) { return 0, nil }, + MockClose: func() error { return nil }, + }, nil + }, + MockRemoveAll: func() error { return nil }, + }, + }, + } + + for _, test := range testCases { + tt := test + t.Run(tt.name, func(t *testing.T) { + _, err := DumpKubeConfig(sampleKubeConfig, "ttt", tt.fs) + assert.Equal(t, tt.expectedErr, err) + }) + } +} + type fakeReaderWriter struct { readErr error writeErr error