[#20] Add abstraction to kubernetes interactions
Object Cluster will be a entry point to intractions with kubernetes It will provide: - kubectl abstraction which will emulate kubectl apply and other commands (in the future) - kubernets client-go kubernetes interface which has all operations, including CRUD to kubernetes apps, core, extentions, etc... objects Change-Id: Ie2961f68a160e720c264c622c1124f283bded161
This commit is contained in:
parent
0501a3b919
commit
9afeccb785
68
pkg/k8s/client/client.go
Normal file
68
pkg/k8s/client/client.go
Normal file
@ -0,0 +1,68 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubectl"
|
||||
k8sutils "opendev.org/airship/airshipctl/pkg/k8s/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// buffDir is a directory that is used as a tmp storage for kubectl
|
||||
buffDir = ".airship"
|
||||
)
|
||||
|
||||
// Interface provides an abstraction layer to interactions
|
||||
// with kubernetes clusters by getting Clientset which includes
|
||||
// all kubernetes core objects with standard operations and kubectl
|
||||
// interface that is built on top of kubectl libraries and implements
|
||||
// such kubectl subcommands as kubectl apply (more will be added)
|
||||
type Interface interface {
|
||||
ClientSet() kubernetes.Interface
|
||||
Kubectl() kubectl.Interface
|
||||
}
|
||||
|
||||
// Client is implementation of Cluster interface
|
||||
type Client struct {
|
||||
kubectl kubectl.Interface
|
||||
clientset kubernetes.Interface
|
||||
}
|
||||
|
||||
// ClientSet getter for Clientset interface
|
||||
func (c *Client) ClientSet() kubernetes.Interface {
|
||||
return c.clientset
|
||||
}
|
||||
|
||||
// Kubectl getter for Kubectl interface
|
||||
func (c *Client) Kubectl() kubectl.Interface {
|
||||
return c.kubectl
|
||||
}
|
||||
|
||||
// NewClient returns Cluster interface with Kubectl
|
||||
// and Clientset interfaces initialized
|
||||
func NewClient(as *environment.AirshipCTLSettings) (Interface, error) {
|
||||
f := k8sutils.FactoryFromKubeconfigPath(as.KubeConfigPath())
|
||||
kctl := kubectl.NewKubectl(f).
|
||||
WithBufferDir(filepath.Dir(as.AirshipConfigPath()) + buffDir)
|
||||
clientSet, err := f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &Client{}
|
||||
client.SetClientset(clientSet)
|
||||
client.SetKubectl(kctl)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// SetClientset setter for Clientset interface
|
||||
func (c *Client) SetClientset(cs kubernetes.Interface) {
|
||||
c.clientset = cs
|
||||
}
|
||||
|
||||
// SetKubectl setter for Kubectl interface
|
||||
func (c *Client) SetKubectl(kctl kubectl.Interface) {
|
||||
c.kubectl = kctl
|
||||
}
|
36
pkg/k8s/client/client_test.go
Normal file
36
pkg/k8s/client/client_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
)
|
||||
|
||||
const (
|
||||
kubeconfigPath = "testdata/kubeconfig.yaml"
|
||||
airshipConfigDir = "testdata"
|
||||
)
|
||||
|
||||
func TestNewclient(t *testing.T) {
|
||||
settings := &environment.AirshipCTLSettings{}
|
||||
settings.InitConfig()
|
||||
|
||||
akp, err := filepath.Abs(kubeconfigPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
adir, err := filepath.Abs(airshipConfigDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
settings.SetAirshipConfigPath(adir)
|
||||
settings.SetKubeConfigPath(akp)
|
||||
|
||||
client, err := NewClient(settings)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, client)
|
||||
assert.NotNil(t, client.ClientSet())
|
||||
assert.NotNil(t, client.Kubectl())
|
||||
}
|
19
pkg/k8s/client/testdata/kubeconfig.yaml
vendored
Normal file
19
pkg/k8s/client/testdata/kubeconfig.yaml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1Ea3lPVEUzTURNd09Wb1hEVEk1TURreU5qRTNNRE13T1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUZyCkdxM0kyb2dZci81Y01Udy9Na1pORTNWQURzdEdyU240WjU2TDhPUGhMcUhDN2t1dno2dVpES3dCSGtGeTBNK2MKRXIzd2piUGE1aTV5NmkyMGtxSHBVMjdPZTA0dzBXV2s4N0RSZVlWaGNoZVJHRXoraWt3SndIcGRmMjJVemZNKwpkSDBzaUhuMVd6UnovYk4za3hMUzJlMnZ2U1Y3bmNubk1YRUd4OXV0MUY0NThHeWxxdmxXTUlWMzg5Q2didXFDCkcwcFdiMTBLM0RVZWdiT25Xa1FmSm5sTWRRVVZDUVdZZEZaaklrcWtkWi9hVTRobkNEV01oZXNWRnFNaDN3VVAKczhQay9BNWh1ZFFPbnFRNDVIWXZLdjZ5RjJWcDUyWExBRUx3NDJ4aVRKZlh0V1h4eHR6cU4wY1lyL2VxeS9XMQp1YVVGSW5xQjFVM0JFL1oxbmFrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFKUUVKQVBLSkFjVDVuK3dsWGJsdU9mS0J3c2gKZTI4R1c5R2QwM0N0NGF3RzhzMXE1ZHNua2tpZmVTUENHVFZ1SXF6UTZDNmJaSk9SMDMvVEl5ejh6NDJnaitDVApjWUZXZkltM2RKTnpRL08xWkdySXZZNWdtcWJtWDlpV0JaU24rRytEOGxubzd2aGMvY0tBRFR5OTMvVU92MThuCkdhMnIrRGJJcHcyTWVBVEl2elpxRS9RWlVSQ25DMmdjUFhTVzFqN2h4R3o1a3ZNcGVDZTdQYVUvdVFvblVHSWsKZ2t6ZzI4NHQvREhUUzc4N1V1SUg5cXBaV09yTFNMOGFBeUxQUHhWSXBteGZmbWRETE9TS2VUemRlTmxoSitUMwowQlBVaHBQTlJBNTNJN0hRQjhVUDR2elNONTkzZ1VFbVlFQ2Jic2RYSzB6ZVR6SDdWWHR2Zmd5WTVWWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://127.0.0.1:6443
|
||||
name: dummycluster_ephemeral
|
||||
contexts:
|
||||
- context:
|
||||
cluster: dummycluster_ephemeral
|
||||
user: kubernetes-admin
|
||||
name: dummy_cluster
|
||||
current-context: dummy_cluster
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
@ -7,12 +7,40 @@ import (
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
// ApplyOptions is a abstraction layer
|
||||
// to ApplyOptions of kubectl/apply package
|
||||
type ApplyOptions struct {
|
||||
ApplyOptions *apply.ApplyOptions
|
||||
}
|
||||
|
||||
func (ao *ApplyOptions) SetDryRun(dryRun bool) {
|
||||
ao.ApplyOptions.DryRun = dryRun
|
||||
}
|
||||
|
||||
func (ao *ApplyOptions) SetPrune(label string) {
|
||||
if label != "" {
|
||||
ao.ApplyOptions.Prune = true
|
||||
ao.ApplyOptions.Selector = label
|
||||
} else {
|
||||
ao.ApplyOptions.Prune = false
|
||||
}
|
||||
}
|
||||
|
||||
// SetSourceFiles sets files to read for kubectl apply command
|
||||
func (ao *ApplyOptions) SetSourceFiles(fileNames []string) {
|
||||
ao.ApplyOptions.DeleteOptions.Filenames = fileNames
|
||||
}
|
||||
|
||||
func (ao *ApplyOptions) Run() error {
|
||||
return ao.ApplyOptions.Run()
|
||||
}
|
||||
|
||||
// NewApplyOptions is a helper function that Creates ApplyOptions of kubectl apply module
|
||||
// Values set here, are default, and do not conflict with each other, can be used if you
|
||||
// need `kubectl apply` functionality without calling executing command in shell
|
||||
// To function properly, you may need to specify files from where to read the resources:
|
||||
// DeleteOptions.Filenames of returned object has to be set for that
|
||||
func NewApplyOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) (*apply.ApplyOptions, error) {
|
||||
// SetSourceFiles of returned object has to be used for that
|
||||
func NewApplyOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) (*ApplyOptions, error) {
|
||||
o := apply.NewApplyOptions(streams)
|
||||
o.ServerSideApply = false
|
||||
o.ForceConflicts = false
|
||||
@ -74,5 +102,5 @@ func NewApplyOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) (*a
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o, nil
|
||||
return &ApplyOptions{ApplyOptions: o}, nil
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ func TestApplyOptionsRun(t *testing.T) {
|
||||
|
||||
aa, err := kubectl.NewApplyOptions(f, streams)
|
||||
require.NoError(t, err, "Could not build ApplyAdapter")
|
||||
aa.DryRun = true
|
||||
aa.DeleteOptions.Filenames = []string{filenameRC}
|
||||
aa.SetDryRun(true)
|
||||
aa.SetSourceFiles([]string{filenameRC})
|
||||
assert.NoError(t, aa.Run())
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"k8s.io/kubectl/pkg/cmd/apply"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Apply(docs []document.Document, ao *apply.ApplyOptions) error
|
||||
Apply(docs []document.Document, ao *ApplyOptions) error
|
||||
ApplyOptions() (*ApplyOptions, error)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"os"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/kubectl/pkg/cmd/apply"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"sigs.k8s.io/kustomize/v3/pkg/fs"
|
||||
|
||||
@ -24,7 +23,7 @@ type Kubectl struct {
|
||||
bufferDir string
|
||||
}
|
||||
|
||||
// NewKubectlFromKubeconfigPath builds an instance
|
||||
// NewKubectl builds an instance
|
||||
// of Kubectl struct from Path to kubeconfig file
|
||||
func NewKubectl(f cmdutil.Factory) *Kubectl {
|
||||
return &Kubectl{
|
||||
@ -44,7 +43,7 @@ func (kubectl *Kubectl) WithBufferDir(bd string) *Kubectl {
|
||||
}
|
||||
|
||||
// Apply is abstraction to kubectl apply command
|
||||
func (kubectl *Kubectl) Apply(docs []document.Document, ao *apply.ApplyOptions) error {
|
||||
func (kubectl *Kubectl) Apply(docs []document.Document, ao *ApplyOptions) error {
|
||||
tf, err := kubectl.TempFile(kubectl.bufferDir, "initinfra")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -65,12 +64,12 @@ func (kubectl *Kubectl) Apply(docs []document.Document, ao *apply.ApplyOptions)
|
||||
return err
|
||||
}
|
||||
}
|
||||
ao.DeleteOptions.Filenames = []string{tf.Name()}
|
||||
ao.SetSourceFiles([]string{tf.Name()})
|
||||
return ao.Run()
|
||||
}
|
||||
|
||||
// ApplyOptions is a wrapper over kubectl ApplyOptions, used to build
|
||||
// new options from the factory and iostreams defined in Kubectl container
|
||||
func (kubectl *Kubectl) ApplyOptions() (*apply.ApplyOptions, error) {
|
||||
func (kubectl *Kubectl) ApplyOptions() (*ApplyOptions, error) {
|
||||
return NewApplyOptions(kubectl.Factory, kubectl.IOStreams)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func TestApply(t *testing.T) {
|
||||
kctl.Factory = f
|
||||
ao, err := kctl.ApplyOptions()
|
||||
require.NoError(t, err, "failed to get documents from bundle")
|
||||
ao.DryRun = true
|
||||
ao.SetDryRun(true)
|
||||
|
||||
b := testutil.NewTestBundle(t, fixtureDir)
|
||||
docs, err := b.GetByAnnotation("airshipit.org/initinfra")
|
||||
|
Loading…
Reference in New Issue
Block a user