Add kubeconfig builder
Kubeconfig builder will be a single source of kubeconfig, allowing to make a decision how to build kubeconfig based on different parameters, such as path to kubeconfig, dynamic kubeconfig options, or kubeconfig based on document bundle. Change-Id: Ia63e11a6f0b327e283d3e7fce169a35d54684dfb
This commit is contained in:
parent
fe1f1f6955
commit
1f932861d4
38
pkg/api/v1alpha1/cluster_map_types.go
Normal file
38
pkg/api/v1alpha1/cluster_map_types.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ClusterMap represents cluster defined for this manifest
|
||||
type ClusterMap struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
// Keys in this map MUST correspond to context names in kubeconfigs provided
|
||||
Map map[string]*Cluster
|
||||
}
|
||||
|
||||
// Cluster uniquely identifies a cluster and its parent cluster
|
||||
type Cluster struct {
|
||||
// Parent is a key in ClusterMap.Map that identifies the name of the parent(management) cluster
|
||||
Parent string `json:"parent,omitempty"`
|
||||
// DynamicKubeConfig kubeconfig allows to get kubeconfig from parent cluster, instead
|
||||
// expecting it to be in document bundle. Parent kubeconfig will be used to get kubeconfig
|
||||
DynamicKubeConfig bool `json:"dynamicKubeConf,omitempty"`
|
||||
}
|
@ -48,6 +48,7 @@ func init() {
|
||||
&KubernetesApply{},
|
||||
&ImageConfiguration{},
|
||||
&RemoteDirectConfiguration{},
|
||||
&ClusterMap{},
|
||||
)
|
||||
_ = AddToScheme(Scheme) //nolint:errcheck
|
||||
}
|
||||
|
@ -33,8 +33,4 @@ type Phase struct {
|
||||
type PhaseConfig struct {
|
||||
ExecutorRef *corev1.ObjectReference `json:"executorRef"`
|
||||
DocumentEntryPoint string `json:"documentEntryPoint"`
|
||||
// Name used to identify a cluster that the phase belongs to
|
||||
ClusterName string `json:"clusterName"`
|
||||
// ClusterNamespace identifies the namespace that the phase belongs to
|
||||
ClusterNamespace string `json:"clusterNamespace"`
|
||||
}
|
||||
|
@ -56,6 +56,61 @@ func (in *ApplyWaitOptions) DeepCopy() *ApplyWaitOptions {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
|
||||
func (in *Cluster) DeepCopy() *Cluster {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Cluster)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterMap) DeepCopyInto(out *ClusterMap) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Map != nil {
|
||||
in, out := &in.Map, &out.Map
|
||||
*out = make(map[string]*Cluster, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *Cluster
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(Cluster)
|
||||
**out = **in
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterMap.
|
||||
func (in *ClusterMap) DeepCopy() *ClusterMap {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterMap)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ClusterMap) 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 *Clusterctl) DeepCopyInto(out *Clusterctl) {
|
||||
*out = *in
|
||||
|
@ -33,11 +33,13 @@ var _ ifc.Executor = &ClusterctlExecutor{}
|
||||
|
||||
// ClusterctlExecutor phase executor
|
||||
type ClusterctlExecutor struct {
|
||||
clusterName string
|
||||
dumpRoot string
|
||||
|
||||
Interface
|
||||
bundle document.Bundle
|
||||
options *airshipv1.Clusterctl
|
||||
kubecfg kubeconfig.Interface
|
||||
dumpRoot string
|
||||
bundle document.Bundle
|
||||
options *airshipv1.Clusterctl
|
||||
kubecfg kubeconfig.Interface
|
||||
}
|
||||
|
||||
// RegisterExecutor adds executor to phase executor registry
|
||||
@ -66,11 +68,12 @@ func NewExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
|
||||
return nil, err
|
||||
}
|
||||
return &ClusterctlExecutor{
|
||||
Interface: client,
|
||||
bundle: cfg.ExecutorBundle,
|
||||
options: options,
|
||||
kubecfg: cfg.KubeConfig,
|
||||
dumpRoot: filepath.Dir(cfg.AirshipSettings.AirshipConfigPath),
|
||||
clusterName: cfg.ClusterName,
|
||||
Interface: client,
|
||||
bundle: cfg.ExecutorBundle,
|
||||
options: options,
|
||||
kubecfg: cfg.KubeConfig,
|
||||
dumpRoot: filepath.Dir(cfg.AirshipSettings.AirshipConfigPath),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -171,12 +171,7 @@ func TestExecutorRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Regular Run move",
|
||||
cfgDoc: executorDoc(t, "move"),
|
||||
bundlePath: "testdata/executor_move",
|
||||
expectedEvt: []events.Event{wrapError(airerrors.ErrNotImplemented{})},
|
||||
},
|
||||
// TODO add move tests here
|
||||
}
|
||||
for _, test := range testCases {
|
||||
tt := test
|
||||
|
116
pkg/k8s/kubeconfig/builder.go
Normal file
116
pkg/k8s/kubeconfig/builder.go
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
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 kubeconfig
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/util"
|
||||
)
|
||||
|
||||
// KubeconfigDefaultFileName is a default name for kubeconfig
|
||||
const KubeconfigDefaultFileName = "kubeconfig"
|
||||
|
||||
// NewBuilder returns instance of kubeconfig builder.
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{}
|
||||
}
|
||||
|
||||
// Builder is an object that allows to build a kubeconfig based on various provided sources
|
||||
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
|
||||
type Builder struct {
|
||||
path string
|
||||
bundlePath string
|
||||
clusterName string
|
||||
root string
|
||||
|
||||
clusterMap *v1alpha1.ClusterMap
|
||||
}
|
||||
|
||||
// WithPath allows to set path to prexisting kubeconfig
|
||||
func (b *Builder) WithPath(filePath string) *Builder {
|
||||
b.path = filePath
|
||||
return b
|
||||
}
|
||||
|
||||
// WithBundle allows to set path to bundle that should contain kubeconfig api object
|
||||
func (b *Builder) WithBundle(bundlePath string) *Builder {
|
||||
b.bundlePath = bundlePath
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClusterMap allows to set a parent cluster, that can be used to extract kubeconfig for target cluster
|
||||
func (b *Builder) WithClusterMap(cMap *v1alpha1.ClusterMap) *Builder {
|
||||
b.clusterMap = cMap
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClusterName allows to reach to a cluster to download kubeconfig from there
|
||||
func (b *Builder) WithClusterName(clusterName string) *Builder {
|
||||
b.clusterName = clusterName
|
||||
return b
|
||||
}
|
||||
|
||||
// WithTempRoot allows to set temp root for kubeconfig
|
||||
func (b *Builder) WithTempRoot(root string) *Builder {
|
||||
b.root = root
|
||||
return b
|
||||
}
|
||||
|
||||
// Build builds a kubeconfig interface to be used
|
||||
func (b *Builder) Build() Interface {
|
||||
switch {
|
||||
case b.path != "":
|
||||
fs := document.NewDocumentFs()
|
||||
return NewKubeConfig(FromFile(b.path, fs), InjectFilePath(b.path, fs), InjectTempRoot(b.root))
|
||||
case b.fromParent():
|
||||
// TODO add method that would get kubeconfig from parent cluster and glue it together
|
||||
// with parent kubeconfig if needed
|
||||
return NewKubeConfig(func() ([]byte, error) {
|
||||
return nil, errors.ErrNotImplemented{}
|
||||
})
|
||||
case b.bundlePath != "":
|
||||
return NewKubeConfig(FromBundle(b.bundlePath), InjectTempRoot(b.root))
|
||||
default:
|
||||
fs := document.NewDocumentFs()
|
||||
// return default path to kubeconfig file in airship workdir
|
||||
path := filepath.Join(util.UserHomeDir(), config.AirshipConfigDir, KubeconfigDefaultFileName)
|
||||
return NewKubeConfig(FromFile(path, fs), InjectFilePath(path, fs), InjectTempRoot(b.root))
|
||||
}
|
||||
}
|
||||
|
||||
// fromParent checks if we should get kubeconfig from parent cluster secret
|
||||
func (b *Builder) fromParent() bool {
|
||||
if b.clusterMap == nil {
|
||||
return false
|
||||
}
|
||||
currentCluster, exists := b.clusterMap.Map[b.clusterName]
|
||||
if !exists {
|
||||
log.Debugf("cluster %s is not defined in cluster map %v", b.clusterName, b.clusterMap)
|
||||
return false
|
||||
}
|
||||
// Check if DynamicKubeConfig is enabled, if so that means, we should get kubeconfig
|
||||
// for this cluster from its parent
|
||||
if currentCluster.Parent == "" || !currentCluster.DynamicKubeConfig {
|
||||
log.Debugf("dynamic kubeconfig or parent cluster is not set for cluster %s", b.clusterName)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
127
pkg/k8s/kubeconfig/builder_test.go
Normal file
127
pkg/k8s/kubeconfig/builder_test.go
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
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 kubeconfig_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/util"
|
||||
)
|
||||
|
||||
func TestBuilder(t *testing.T) {
|
||||
t.Run("Only bundle", func(t *testing.T) {
|
||||
builder := kubeconfig.NewBuilder().WithBundle("testdata")
|
||||
kube := builder.Build()
|
||||
require.NotNil(t, kube)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err := kube.Write(buf)
|
||||
require.NoError(t, err)
|
||||
// check that kubeconfig contains expected cluster string
|
||||
assert.Contains(t, buf.String(), "dummycluster_ephemeral")
|
||||
})
|
||||
|
||||
t.Run("Only filepath", func(t *testing.T) {
|
||||
builder := kubeconfig.NewBuilder().WithPath("testdata/kubeconfig")
|
||||
kube := builder.Build()
|
||||
require.NotNil(t, kube)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err := kube.Write(buf)
|
||||
require.NoError(t, err)
|
||||
// check that kubeconfig contains expected cluster string
|
||||
assert.Contains(t, buf.String(), "dummycluster_ephemeral")
|
||||
})
|
||||
|
||||
t.Run("Only cluster map", func(t *testing.T) {
|
||||
childCluster := "child"
|
||||
parentCluster := "parent"
|
||||
clusterMap := &v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
childCluster: {
|
||||
Parent: parentCluster,
|
||||
DynamicKubeConfig: true,
|
||||
},
|
||||
parentCluster: {
|
||||
DynamicKubeConfig: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
builder := kubeconfig.NewBuilder().
|
||||
WithClusterMap(clusterMap).
|
||||
WithClusterName(childCluster)
|
||||
kube := builder.Build()
|
||||
// This should not be implemented yet, and we need to check that we are getting there
|
||||
require.NotNil(t, kube)
|
||||
filePath, cleanup, err := kube.GetFile()
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "not implemented")
|
||||
assert.Equal(t, "", filePath)
|
||||
require.Nil(t, cleanup)
|
||||
})
|
||||
|
||||
t.Run("No current cluster, fall to default", func(t *testing.T) {
|
||||
clusterMap := &v1alpha1.ClusterMap{}
|
||||
builder := kubeconfig.NewBuilder().
|
||||
WithClusterMap(clusterMap).
|
||||
WithClusterName("some-cluster")
|
||||
kube := builder.Build()
|
||||
// We should get a default value for cluster since we don't have some-cluster set
|
||||
actualPath, cleanup, err := kube.GetFile()
|
||||
require.NoError(t, err)
|
||||
defer cleanup()
|
||||
path := filepath.Join(util.UserHomeDir(), config.AirshipConfigDir, kubeconfig.KubeconfigDefaultFileName)
|
||||
assert.Equal(t, path, actualPath)
|
||||
})
|
||||
|
||||
t.Run("No parent cluster is defined, fall to default", func(t *testing.T) {
|
||||
childCluster := "child"
|
||||
clusterMap := &v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
childCluster: {
|
||||
DynamicKubeConfig: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
builder := kubeconfig.NewBuilder().
|
||||
WithClusterMap(clusterMap).
|
||||
WithClusterName(childCluster)
|
||||
kube := builder.Build()
|
||||
// We should get a default value for cluster, as we can't find parent cluster
|
||||
actualPath, cleanup, err := kube.GetFile()
|
||||
defer cleanup()
|
||||
require.NoError(t, err)
|
||||
path := filepath.Join(util.UserHomeDir(), config.AirshipConfigDir, kubeconfig.KubeconfigDefaultFileName)
|
||||
assert.Equal(t, path, actualPath)
|
||||
})
|
||||
|
||||
t.Run("Default source", func(t *testing.T) {
|
||||
builder := kubeconfig.NewBuilder()
|
||||
kube := builder.Build()
|
||||
// When ClusterMap is specified, but it doesn't have cluster-name defined, and no
|
||||
// other sources provided,
|
||||
actualPath, cleanup, err := kube.GetFile()
|
||||
defer cleanup()
|
||||
require.NoError(t, err)
|
||||
path := filepath.Join(util.UserHomeDir(), config.AirshipConfigDir, kubeconfig.KubeconfigDefaultFileName)
|
||||
assert.Equal(t, path, actualPath)
|
||||
})
|
||||
}
|
19
pkg/k8s/kubeconfig/testdata/kubeconfig
vendored
Normal file
19
pkg/k8s/kubeconfig/testdata/kubeconfig
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://10.23.25.101:6443
|
||||
name: dummycluster_ephemeral
|
||||
contexts:
|
||||
- context:
|
||||
cluster: dummycluster_ephemeral
|
||||
user: kubernetes-admin
|
||||
name: dummy_cluster
|
||||
current-context: dummy_cluster
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
@ -18,6 +18,7 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
@ -55,6 +56,7 @@ type ExecutorConfig struct {
|
||||
PhaseName string
|
||||
ClusterName string
|
||||
|
||||
ClusterMap *v1alpha1.ClusterMap
|
||||
ExecutorDocument document.Document
|
||||
ExecutorBundle document.Bundle
|
||||
AirshipSettings *environment.AirshipCTLSettings
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
clusterctl "opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
@ -31,7 +30,6 @@ import (
|
||||
k8sutils "opendev.org/airship/airshipctl/pkg/k8s/utils"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
"opendev.org/airship/airshipctl/pkg/util"
|
||||
)
|
||||
|
||||
// ExecutorRegistry returns map with executor factories
|
||||
@ -107,6 +105,28 @@ func (p *Cmd) GetPhase(name string) (*airshipv1.Phase, error) {
|
||||
return phaseConfig, nil
|
||||
}
|
||||
|
||||
// GetClusterMap returns cluster map object
|
||||
func (p *Cmd) GetClusterMap() (*airshipv1.ClusterMap, error) {
|
||||
bundle, err := p.getBundle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterMap := &airshipv1.ClusterMap{}
|
||||
selector, err := document.NewSelector().ByObject(clusterMap, airshipv1.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc, err := bundle.SelectOne(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = doc.ToAPIObject(clusterMap, airshipv1.Scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterMap, nil
|
||||
}
|
||||
|
||||
// GetExecutor referenced in a phase configuration
|
||||
func (p *Cmd) GetExecutor(phase *airshipv1.Phase) (ifc.Executor, error) {
|
||||
bundle, err := p.getBundle()
|
||||
@ -131,9 +151,14 @@ func (p *Cmd) GetExecutor(phase *airshipv1.Phase) (ifc.Executor, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
executorDocBundle, err := document.NewBundleByPath(filepath.Join(targetPath, phaseConfig.DocumentEntryPoint))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var executorDocBundle document.Bundle
|
||||
// if entrypoint is defined use it, if not, just pass nil bundle, executors should be ready for that
|
||||
if phaseConfig.DocumentEntryPoint != "" {
|
||||
bundlePath := filepath.Join(targetPath, phaseConfig.DocumentEntryPoint)
|
||||
executorDocBundle, err = document.NewBundleByPath(bundlePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if p.Registry == nil {
|
||||
p.Registry = DefaultExecutorRegistry
|
||||
@ -143,25 +168,33 @@ func (p *Cmd) GetExecutor(phase *airshipv1.Phase) (ifc.Executor, error) {
|
||||
if !found {
|
||||
return nil, ErrExecutorNotFound{GVK: refGVK}
|
||||
}
|
||||
meta, err := p.Config.CurrentContextManifestMetadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cMap, err := p.GetClusterMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kubeConfPath := p.AirshipCTLSettings.Config.KubeConfigPath()
|
||||
homeDir := util.UserHomeDir()
|
||||
workDir := filepath.Join(homeDir, config.AirshipConfigDir)
|
||||
fs := document.NewDocumentFs()
|
||||
source := kubeconfig.FromFile(kubeConfPath, fs)
|
||||
fileOption := kubeconfig.InjectFilePath(kubeConfPath, fs)
|
||||
tempRootOption := kubeconfig.InjectTempRoot(workDir)
|
||||
kubeConfig := kubeconfig.NewKubeConfig(source, fileOption, tempRootOption)
|
||||
|
||||
// TODO add function to decide on how to build kubeconfig instead of hardcoding it here,
|
||||
// when more kubeconfigs sources are available.
|
||||
return executorFactory(ifc.ExecutorConfig{
|
||||
ExecutorBundle: executorDocBundle,
|
||||
PhaseName: phase.Name,
|
||||
ExecutorDocument: executorDoc,
|
||||
AirshipSettings: p.AirshipCTLSettings,
|
||||
KubeConfig: kubeConfig,
|
||||
})
|
||||
kubeConfig := kubeconfig.NewBuilder().
|
||||
// TODO add kubeconfig flags path here, when kubeconfig flag is not controlled
|
||||
// by config module during config loading.
|
||||
WithBundle(meta.PhaseMeta.Path).
|
||||
WithClusterMap(cMap).
|
||||
WithClusterName(phase.ClusterName).
|
||||
WithTempRoot(filepath.Dir(p.Config.LoadedConfigPath())).
|
||||
Build()
|
||||
return executorFactory(
|
||||
ifc.ExecutorConfig{
|
||||
ExecutorBundle: executorDocBundle,
|
||||
PhaseName: phase.Name,
|
||||
ExecutorDocument: executorDoc,
|
||||
AirshipSettings: p.AirshipCTLSettings,
|
||||
KubeConfig: kubeConfig,
|
||||
ClusterName: phase.ClusterName,
|
||||
ClusterMap: cMap,
|
||||
})
|
||||
}
|
||||
|
||||
// Exec starts executor goroutine and processes the events
|
||||
|
9
pkg/phase/testdata/valid_site/phases/cluster_map.yaml
vendored
Normal file
9
pkg/phase/testdata/valid_site/phases/cluster_map.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
name: clusterctl-v1
|
||||
plan:
|
||||
target:
|
||||
parent: ephemeral
|
||||
dynamicKubeConf: false
|
||||
ephemeral: {}
|
@ -5,3 +5,4 @@ resources:
|
||||
- capi_init.yaml
|
||||
- clusterctl.yaml
|
||||
- kubernetes_apply.yaml
|
||||
- cluster_map.yaml
|
||||
|
Loading…
x
Reference in New Issue
Block a user