├── .gitignore ├── .travis.yml ├── DCO ├── LICENSE ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── k8s ├── admissionregistration │ ├── admissionregistration.go │ ├── admissionregistration_test.go │ ├── mutationwebhooks.go │ ├── mutationwebhooks_v1beta1.go │ ├── validatingwebhooks.go │ └── validatingwebhooks_v1beta1.go ├── anthos │ ├── anthos.go │ ├── anthosonprem.go │ ├── clusters.go │ └── machines.go ├── apiextensions │ ├── apiextensions.go │ ├── apiextensions_test.go │ ├── crds.go │ └── crds_v1beta1.go ├── apps │ ├── apps.go │ ├── apps_test.go │ ├── daemonsets.go │ ├── deployments.go │ ├── replicasets.go │ └── statefulsets.go ├── autopilot │ ├── actionaproval.go │ ├── autopilot.go │ ├── autopilot_test.go │ ├── autopilotrule.go │ └── autopilotruleobject.go ├── batch │ ├── batch.go │ ├── batch_test.go │ ├── cron.go │ ├── cron_v1beta1.go │ └── jobs.go ├── common │ ├── pod.go │ ├── pvc.go │ └── utils.go ├── core │ ├── configmap │ │ ├── configmap.go │ │ ├── configmap_lock_v1.go │ │ ├── configmap_lock_v1_test.go │ │ ├── configmap_lock_v2.go │ │ ├── configmap_lock_v2_test.go │ │ ├── configmap_test.go │ │ └── types.go │ ├── configmaps.go │ ├── core.go │ ├── core_test.go │ ├── csr.go │ ├── endpoints.go │ ├── events.go │ ├── events_test.go │ ├── limitrange.go │ ├── namespaces.go │ ├── namespaces_test.go │ ├── networkpolicy.go │ ├── nodes.go │ ├── persistentvolumeclaims.go │ ├── persistenvolumeclaims_test.go │ ├── pods.go │ ├── secrets.go │ ├── serviceaccounts.go │ ├── services.go │ └── util.go ├── doc.go ├── dynamic │ ├── dynamic.go │ └── dynamic_test.go ├── errors │ └── errors.go ├── externalsnapshotter │ ├── externalsnapshotter.go │ ├── externalsnapshotter_test.go │ ├── volumesnapshotclasses.go │ ├── volumesnapshotcontents.go │ └── volumesnapshots.go ├── externalstorage │ ├── externalstorage.go │ ├── externalstorage_test.go │ └── snapshot.go ├── kdmp │ ├── backuplocationmaintenance.go │ ├── dataexport.go │ ├── kdmp.go │ ├── resourcebackup.go │ ├── resourceexport.go │ ├── volumebackup.go │ └── volumebackupdelete.go ├── kubevirt-dynamic │ ├── datavolume.go │ ├── datavolume_test.go │ ├── kubevirt.go │ ├── kubevirt_test.go │ ├── virtualmachine.go │ ├── virtualmachine_test.go │ ├── virtualmachineinstance.go │ ├── virtualmachineinstance_test.go │ ├── virtualmachineinstancemigration.go │ └── virtualmachineinstancemigration_test.go ├── kubevirt │ ├── kubevirt.go │ ├── virtualmachine.go │ └── virtualmachineinstance.go ├── networking │ ├── ingress.go │ ├── networking.go │ └── networking_test.go ├── openshift │ ├── config.go │ ├── deploymentconfig.go │ ├── openshift.go │ ├── openshift_test.go │ └── securitycontextconstraints.go ├── operator │ ├── operator.go │ ├── operator_test.go │ ├── portworxdiag.go │ ├── storagecluster.go │ └── storagenode.go ├── operatormarketplace │ ├── catalogsource.go │ ├── clusterserviceversion.go │ ├── installplan.go │ ├── operatorgroup.go │ ├── operatormarketplace.go │ └── subscription.go ├── policy │ ├── poddisruptionbudget.go │ ├── poddisruptionbudget_v1beta1.go │ ├── podsecuritypolicy.go │ └── policy.go ├── prometheus │ ├── alertmanager.go │ ├── alertmanagerconfig.go │ ├── prometheus.go │ ├── prometheus_test.go │ ├── prometheuspods.go │ ├── prometheusrules.go │ └── servicemonitor.go ├── rbac │ ├── clusterrolebindings.go │ ├── clusterroles.go │ ├── rbac.go │ ├── rbac_test.go │ ├── rolebindings.go │ └── roles.go ├── storage │ ├── csidriver.go │ ├── storage.go │ ├── storage_test.go │ ├── storageclass.go │ └── volumeattachments.go ├── stork │ ├── action.go │ ├── applicationbackuprestore.go │ ├── applicationclone.go │ ├── applicationregistration.go │ ├── backuplocation.go │ ├── clusterdomains.go │ ├── clusterpair.go │ ├── groupsnapshot.go │ ├── migration.go │ ├── namespacedschedulepolicy.go │ ├── platformcredential.go │ ├── resourcetransformation.go │ ├── rule.go │ ├── schedulepolicy.go │ ├── snapshotschedule.go │ ├── stork.go │ ├── stork_test.go │ └── volumesnapshot.go ├── talisman │ ├── talisman.go │ ├── talisman_test.go │ └── volumeplacementstrategy.go ├── tektoncd │ ├── pipeline.go │ ├── task.go │ └── tektoncd.go └── testutil │ └── kind.go └── task ├── task.go └── task_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .idea 3 | vendor 4 | *.sw* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | language: go 4 | go: 5 | - "1.22.6" 6 | before_install: 7 | - echo $PATH 8 | - which go 9 | - ls -al $(which go) 10 | - sudo find / -name go 11 | install: 12 | - ls -al $(which go) 13 | - $(which go) version 14 | - make fetch-tools 15 | - make vendor 16 | script: 17 | - make git-validation 18 | - make fmt 19 | - make vet 20 | - make test 21 | - make lint 22 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO := go 2 | 3 | HAS_GOMODULES := $(shell go help mod why 2> /dev/null) 4 | 5 | ifdef HAS_GOMODULES 6 | export GO111MODULE=on 7 | export GOFLAGS = -mod=vendor 8 | else 9 | $(warn vendor import can only be done on go 1.11+ which supports go modules) 10 | endif 11 | 12 | .PHONY: all build install clean test format vet golint lint errcheck vendor 13 | 14 | 15 | # Tools 16 | # 17 | # In module mode, 'go get' has a side-effect of updating the go.mod 18 | # file. We do not want to update go.mod when installing tools. 19 | # As a workaround, when installing a tool, cd to /tmp and turn off 20 | # module mode. This should be solved in: 21 | # https://github.com/golang/go/issues/30515 22 | # https://github.com/golang/go/issues/24250 23 | 24 | fetch-tools: 25 | mkdir -p tools 26 | (cd tools && $(GO) install -mod=readonly golang.org/x/lint/golint@v0.0.0-20210508222113-6edffad5e616) 27 | (cd tools && $(GO) install -mod=readonly github.com/kisielk/errcheck@v1.7.0) 28 | (cd tools && $(GO) install -mod=readonly github.com/vbatts/git-validation@v1.2.0) 29 | 30 | # Deliverables 31 | 32 | fmt: 33 | $(GO) fmt ./... | wc -l | grep 0 34 | 35 | vet: 36 | $(GO) vet ./... 37 | 38 | test: $(GOPATH)/bin/kind 39 | $(GO) test ./... 40 | 41 | git-validation: 42 | git-validation -run DCO,short-subject 43 | 44 | lint: 45 | golint `go list ./...` 46 | 47 | errcheck: 48 | errcheck -verbose -blank ./... 49 | 50 | vendor: vendor-tidy 51 | @echo "Updating vendor tree" 52 | go mod vendor 53 | sed -i '1 i\// +build skipcompile\n' vendor/kubevirt.io/client-go/kubecli/kubevirt_test_utils.go 54 | 55 | vendor-tidy: 56 | @echo "Removing unused files in vendor tree" 57 | go mod tidy 58 | 59 | $(GOPATH)/bin/kind: 60 | @echo "Installing kind" 61 | go install -mod=readonly sigs.k8s.io/kind@v0.16.0 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Travis branch](https://img.shields.io/travis/portworx/sched-ops/master.svg)](https://travis-ci.org/portworx/sched-ops) [![Go Report Card](https://goreportcard.com/badge/github.com/portworx/sched-ops)](https://goreportcard.com/report/github.com/portworx/sched-ops) 2 | 3 | sched-ops is a package for useful Kubernetes workflows using the [client-go](https://github.com/kubernetes/client-go) library. 4 | -------------------------------------------------------------------------------- /k8s/admissionregistration/admissionregistration_test.go: -------------------------------------------------------------------------------- 1 | package admissionregistration 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/admissionregistration/mutationwebhooks.go: -------------------------------------------------------------------------------- 1 | package admissionregistration 2 | 3 | import ( 4 | "context" 5 | 6 | hook "k8s.io/api/admissionregistration/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // MutatingWebhookConfigurationOps is interface to perform CRUD ops on mutatting webhook controller 11 | type MutatingWebhookConfigurationOps interface { 12 | // GetMutatingWebhookConfiguration returns a given MutatingWebhookConfiguration 13 | GetMutatingWebhookConfiguration(name string) (*hook.MutatingWebhookConfiguration, error) 14 | // CreateMutatingWebhookConfiguration creates given MutatingWebhookConfiguration 15 | CreateMutatingWebhookConfiguration(req *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) 16 | // UpdateMutatingWebhookConfiguration updates given MutatingWebhookConfiguration 17 | UpdateMutatingWebhookConfiguration(*hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) 18 | // DeleteMutatingWebhookConfiguration deletes given MutatingWebhookConfiguration 19 | DeleteMutatingWebhookConfiguration(name string) error 20 | } 21 | 22 | // GetMutatingWebhookConfiguration returns a given MutatingWebhookConfiguration 23 | func (c *Client) GetMutatingWebhookConfiguration(name string) (*hook.MutatingWebhookConfiguration, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.admissionv1.MutatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}) 28 | } 29 | 30 | // CreateMutatingWebhookConfiguration creates given MutatingWebhookConfiguration 31 | func (c *Client) CreateMutatingWebhookConfiguration(cfg *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.admissionv1.MutatingWebhookConfigurations().Create(context.TODO(), cfg, metav1.CreateOptions{}) 36 | } 37 | 38 | // UpdateMutatingWebhookConfiguration updates given MutatingWebhookConfiguration 39 | func (c *Client) UpdateMutatingWebhookConfiguration(cfg *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.admissionv1.MutatingWebhookConfigurations().Update(context.TODO(), cfg, metav1.UpdateOptions{}) 44 | } 45 | 46 | // DeleteMutatingWebhookConfiguration deletes given MutatingWebhookConfiguration 47 | func (c *Client) DeleteMutatingWebhookConfiguration(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.admissionv1.MutatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}) 52 | } 53 | -------------------------------------------------------------------------------- /k8s/admissionregistration/mutationwebhooks_v1beta1.go: -------------------------------------------------------------------------------- 1 | package admissionregistration 2 | 3 | import ( 4 | "context" 5 | 6 | hook "k8s.io/api/admissionregistration/v1beta1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // MutatingWebhookConfigurationV1beta1Ops is interface to perform CRUD ops on mutatting webhook controller 11 | type MutatingWebhookConfigurationV1beta1Ops interface { 12 | // GetMutatingWebhookConfigurationV1beta1 returns a given MutatingWebhookConfiguration 13 | GetMutatingWebhookConfigurationV1beta1(name string) (*hook.MutatingWebhookConfiguration, error) 14 | // CreateMutatingWebhookConfigurationV1beta1 creates given MutatingWebhookConfiguration 15 | CreateMutatingWebhookConfigurationV1beta1(req *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) 16 | // UpdateMutatingWebhookConfigurationV1beta1 updates given MutatingWebhookConfiguration 17 | UpdateMutatingWebhookConfigurationV1beta1(*hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) 18 | // DeleteMutatingWebhookConfigurationV1beta1 deletes given MutatingWebhookConfiguration 19 | DeleteMutatingWebhookConfigurationV1beta1(name string) error 20 | } 21 | 22 | // GetMutatingWebhookConfigurationV1beta1 returns a given MutatingWebhookConfiguration 23 | func (c *Client) GetMutatingWebhookConfigurationV1beta1(name string) (*hook.MutatingWebhookConfiguration, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.admissionv1beta1.MutatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}) 28 | } 29 | 30 | // CreateMutatingWebhookConfigurationV1beta1 creates given MutatingWebhookConfiguration 31 | func (c *Client) CreateMutatingWebhookConfigurationV1beta1(cfg *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.admissionv1beta1.MutatingWebhookConfigurations().Create(context.TODO(), cfg, metav1.CreateOptions{}) 36 | } 37 | 38 | // UpdateMutatingWebhookConfigurationV1beta1 updates given MutatingWebhookConfiguration 39 | func (c *Client) UpdateMutatingWebhookConfigurationV1beta1(cfg *hook.MutatingWebhookConfiguration) (*hook.MutatingWebhookConfiguration, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.admissionv1beta1.MutatingWebhookConfigurations().Update(context.TODO(), cfg, metav1.UpdateOptions{}) 44 | } 45 | 46 | // DeleteMutatingWebhookConfigurationV1beta1 deletes given MutatingWebhookConfiguration 47 | func (c *Client) DeleteMutatingWebhookConfigurationV1beta1(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.admissionv1beta1.MutatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}) 52 | } 53 | -------------------------------------------------------------------------------- /k8s/admissionregistration/validatingwebhooks.go: -------------------------------------------------------------------------------- 1 | package admissionregistration 2 | 3 | import ( 4 | "context" 5 | 6 | hook "k8s.io/api/admissionregistration/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ValidatingWebhookConfigurationOps is an interface to perform CRUD ops on mutatting webhook controller 11 | type ValidatingWebhookConfigurationOps interface { 12 | // GetValidatingWebhookConfiguration returns given ValidatingWebhookConfiguration 13 | GetValidatingWebhookConfiguration(name string) (*hook.ValidatingWebhookConfiguration, error) 14 | // CreateValidatingWebhookConfiguration creates given ValidatingWebhookConfiguration 15 | CreateValidatingWebhookConfiguration(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) 16 | // UpdateValidatingWebhookConfiguration updates given ValidatingWebhookConfiguration 17 | UpdateValidatingWebhookConfiguration(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) 18 | // DeleteValidatingWebhookConfiguration deletes given ValidatingWebhookConfiguration 19 | DeleteValidatingWebhookConfiguration(name string) error 20 | } 21 | 22 | // GetValidatingWebhookConfiguration returns given ValidatingWebhookConfiguration 23 | func (c *Client) GetValidatingWebhookConfiguration(name string) (*hook.ValidatingWebhookConfiguration, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.admissionv1.ValidatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}) 28 | } 29 | 30 | // CreateValidatingWebhookConfiguration creates given ValidatingWebhookConfiguration 31 | func (c *Client) CreateValidatingWebhookConfiguration(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.admissionv1.ValidatingWebhookConfigurations().Create(context.TODO(), cfg, metav1.CreateOptions{}) 36 | } 37 | 38 | // UpdateValidatingWebhookConfiguration updates given ValidatingWebhookConfiguration 39 | func (c *Client) UpdateValidatingWebhookConfiguration(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.admissionv1.ValidatingWebhookConfigurations().Update(context.TODO(), cfg, metav1.UpdateOptions{}) 44 | } 45 | 46 | // DeleteValidatingWebhookConfiguration deletes given ValidatingWebhookConfiguration 47 | func (c *Client) DeleteValidatingWebhookConfiguration(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.admissionv1.ValidatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}) 52 | } 53 | -------------------------------------------------------------------------------- /k8s/admissionregistration/validatingwebhooks_v1beta1.go: -------------------------------------------------------------------------------- 1 | package admissionregistration 2 | 3 | import ( 4 | "context" 5 | 6 | hook "k8s.io/api/admissionregistration/v1beta1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ValidatingWebhookConfigurationV1beta1Ops is interface to perform CRUD ops on mutatting webhook controller 11 | type ValidatingWebhookConfigurationV1beta1Ops interface { 12 | // GetValidatingWebhookConfigurationV1beta1 returns given ValidatingWebhookConfiguration 13 | GetValidatingWebhookConfigurationV1beta1(name string) (*hook.ValidatingWebhookConfiguration, error) 14 | // CreateValidatingWebhookConfigurationV1beta1 creates given ValidatingWebhookConfiguration 15 | CreateValidatingWebhookConfigurationV1beta1(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) 16 | // UpdateValidatingWebhookConfigurationV1beta1 updates given ValidatingWebhookConfiguration 17 | UpdateValidatingWebhookConfigurationV1beta1(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) 18 | // DeleteValidatingWebhookConfigurationV1beta1 deletes given ValidatingWebhookConfiguration 19 | DeleteValidatingWebhookConfigurationV1beta1(name string) error 20 | } 21 | 22 | // GetValidatingWebhookConfigurationV1beta1 returns given ValidatingWebhookConfiguration 23 | func (c *Client) GetValidatingWebhookConfigurationV1beta1(name string) (*hook.ValidatingWebhookConfiguration, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.admissionv1beta1.ValidatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}) 28 | } 29 | 30 | // CreateValidatingWebhookConfigurationV1beta1 creates given ValidatingWebhookConfiguration 31 | func (c *Client) CreateValidatingWebhookConfigurationV1beta1(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.admissionv1beta1.ValidatingWebhookConfigurations().Create(context.TODO(), cfg, metav1.CreateOptions{}) 36 | } 37 | 38 | // UpdateValidatingWebhookConfigurationV1beta1 updates given ValidatingWebhookConfiguration 39 | func (c *Client) UpdateValidatingWebhookConfigurationV1beta1(cfg *hook.ValidatingWebhookConfiguration) (*hook.ValidatingWebhookConfiguration, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.admissionv1beta1.ValidatingWebhookConfigurations().Update(context.TODO(), cfg, metav1.UpdateOptions{}) 44 | } 45 | 46 | // DeleteValidatingWebhookConfigurationV1beta1 deletes given ValidatingWebhookConfiguration 47 | func (c *Client) DeleteValidatingWebhookConfigurationV1beta1(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.admissionv1beta1.ValidatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}) 52 | } 53 | -------------------------------------------------------------------------------- /k8s/anthos/anthosonprem.go: -------------------------------------------------------------------------------- 1 | package anthos 2 | 3 | import "fmt" 4 | 5 | // OnpremOps is an interface to perform Anthos onprem operations 6 | type OnpremOps interface { 7 | // GetVMwareVersionInfo returns Vmware Version Info 8 | GetVMwareVersionInfo(project string, location string) ([]byte, error) 9 | // GetBareMetalVersionInfo returns Bare-metal Version Info 10 | GetBareMetalVersionInfo(project string, location string) ([]byte, error) 11 | // ListVMwareNodePools return VMware pools 12 | ListVMwareNodePools(project string, location string, clustername string) ([]byte, error) 13 | // GetVMwareCluster return vmware cluster 14 | GetVMwareCluster(project string, location string, clustername string) ([]byte, error) 15 | } 16 | 17 | // GetVMwareVersionInfo returns Anthos Vmware Version Info 18 | func (c *Client) GetVMwareVersionInfo(project string, location string) ([]byte, error) { 19 | if err := c.initClient(); err != nil { 20 | return nil, err 21 | } 22 | parent := fmt.Sprintf("projects/%s/locations/%s", project, location) 23 | queryConfigRespone, err := c.projectLocationVmwareClusterService.QueryVersionConfig(parent).Do() 24 | if err != nil { 25 | return nil, fmt.Errorf("unable to retrieve Anthos VMware cluster version. Err: %v", err) 26 | } 27 | return queryConfigRespone.MarshalJSON() 28 | 29 | } 30 | 31 | // GetBareMetalVersionInfo returns Bare-metal Version Info 32 | func (c *Client) GetBareMetalVersionInfo(project string, location string) ([]byte, error) { 33 | if err := c.initClient(); err != nil { 34 | return nil, err 35 | } 36 | parent := fmt.Sprintf("projects/%s/locations/%s", project, location) 37 | queryConfigRespone, err := c.projectLocationBareMetalClusterService.QueryVersionConfig(parent).Do() 38 | if err != nil { 39 | return nil, fmt.Errorf("unable to retrieve Anthos Bare Metal cluster version. Err: %v", err) 40 | } 41 | return queryConfigRespone.MarshalJSON() 42 | 43 | } 44 | 45 | // ListVMwareNodePools return VMware pools 46 | func (c *Client) ListVMwareNodePools(project string, location string, clustername string) ([]byte, error) { 47 | if err := c.initClient(); err != nil { 48 | return nil, err 49 | } 50 | parent := fmt.Sprintf("projects/%s/locations/%s/vmwareClusters/%s", project, location, clustername) 51 | listVmwareNodePoolsResponse, err := c.projectsLocationsVmwareClustersVmwareNodePoolsService.List(parent).Do() 52 | if err != nil { 53 | return nil, fmt.Errorf("unable to list VMware node pools. Err: %v", err) 54 | } 55 | return listVmwareNodePoolsResponse.MarshalJSON() 56 | } 57 | 58 | // GetVMwareCluster return vmware cluster 59 | func (c *Client) GetVMwareCluster(project string, location string, clustername string) ([]byte, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | parent := fmt.Sprintf("projects/%s/locations/%s/vmwareClusters/%s", project, location, clustername) 64 | vmwareClustersResponse, err := c.projectLocationVmwareClusterService.Get(parent).Do() 65 | if err != nil { 66 | return nil, fmt.Errorf("unable to get vmware cluster. Err: %v", err) 67 | } 68 | return vmwareClustersResponse.MarshalJSON() 69 | } 70 | -------------------------------------------------------------------------------- /k8s/anthos/machines.go: -------------------------------------------------------------------------------- 1 | package anthos 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | v1alpha1 "sigs.k8s.io/cluster-api/pkg/apis/deprecated/v1alpha1" 8 | 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/apimachinery/pkg/runtime/schema" 11 | ) 12 | 13 | var ( 14 | machineResource = schema.GroupVersionResource{Group: "cluster.k8s.io", Version: "v1alpha1", Resource: "machines"} 15 | ) 16 | 17 | const ( 18 | DefaultNamespace = "default" 19 | ) 20 | 21 | // MachinesOps is an interface to perform k8s machines operations 22 | type MachineOps interface { 23 | // ListMachines lists all machines in kubernetes cluster 24 | ListMachines(ctx context.Context) (*v1alpha1.MachineList, error) 25 | // GetMachine returns a machine for the given name 26 | GetMachine(ctx context.Context, name string) (*v1alpha1.Machine, error) 27 | // DeleteMachine delete machine for given name 28 | DeleteMachine(ctx context.Context, name string) error 29 | } 30 | 31 | // ListMachines lists all machines in kubernetes cluster 32 | func (c *Client) ListMachines(ctx context.Context) (*v1alpha1.MachineList, error) { 33 | if err := c.initClient(); err != nil { 34 | return nil, err 35 | } 36 | 37 | result, err := c.dynamicClient.Resource(machineResource).Namespace(DefaultNamespace).List(ctx, metav1.ListOptions{}) 38 | if err != nil { 39 | return nil, err 40 | } 41 | jsonData, err := result.MarshalJSON() 42 | if err != nil { 43 | return nil, err 44 | } 45 | machineList := &v1alpha1.MachineList{} 46 | if err := json.Unmarshal(jsonData, machineList); err != nil { 47 | return nil, err 48 | } 49 | 50 | return machineList, err 51 | } 52 | 53 | // GetMachine returns a machine for the given name 54 | func (c *Client) GetMachine(ctx context.Context, name string) (*v1alpha1.Machine, error) { 55 | if err := c.initClient(); err != nil { 56 | return nil, err 57 | } 58 | rawMachine, err := c.dynamicClient.Resource(machineResource).Namespace(DefaultNamespace).Get(ctx, name, metav1.GetOptions{}) 59 | if err != nil { 60 | return nil, err 61 | } 62 | jsonData, err := rawMachine.MarshalJSON() 63 | if err != nil { 64 | return nil, err 65 | } 66 | machine := &v1alpha1.Machine{} 67 | if err := json.Unmarshal(jsonData, machine); err != nil { 68 | return nil, err 69 | } 70 | return machine, nil 71 | } 72 | 73 | // DeleteMachine delete machine for given name 74 | func (c *Client) DeleteMachine(ctx context.Context, name string) error { 75 | if err := c.initClient(); err != nil { 76 | return err 77 | } 78 | return c.dynamicClient.Resource(machineResource).Namespace(DefaultNamespace).Delete(ctx, name, metav1.DeleteOptions{}) 79 | 80 | } 81 | -------------------------------------------------------------------------------- /k8s/apiextensions/apiextensions_test.go: -------------------------------------------------------------------------------- 1 | package apiextensions 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/apps/apps_test.go: -------------------------------------------------------------------------------- 1 | package apps 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/autopilot/actionaproval.go: -------------------------------------------------------------------------------- 1 | package autopilot 2 | 3 | import ( 4 | autv1alpha1 "github.com/libopenstorage/autopilot-api/pkg/apis/autopilot/v1alpha1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // ActionApprovalInterface has methods to work with ActionApproval resources. 9 | type ActionApprovalInterface interface { 10 | // CreateActionApproval creates the ActionApproval object 11 | CreateActionApproval(actionApproval *autv1alpha1.ActionApproval) (*autv1alpha1.ActionApproval, error) 12 | // GetActionApproval gets the ActionApproval for the provided name 13 | GetActionApproval(namespace, name string) (*autv1alpha1.ActionApproval, error) 14 | // UpdateActionApproval updates the ActionApproval 15 | UpdateActionApproval(namespace string, actionApproval *autv1alpha1.ActionApproval) (*autv1alpha1.ActionApproval, error) 16 | // DeleteActionApproval deletes the ActionApproval of the given name 17 | DeleteActionApproval(namespace, name string) error 18 | // ListActionApprovals lists ActionApproval 19 | ListActionApprovals(namespace string) (*autv1alpha1.ActionApprovalList, error) 20 | } 21 | 22 | // CreateActionApproval creates the ActionApproval object 23 | func (c *Client) CreateActionApproval(actionApproval *autv1alpha1.ActionApproval) (*autv1alpha1.ActionApproval, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.autopilot.AutopilotV1alpha1().ActionApprovals(actionApproval.Namespace).Create(actionApproval) 28 | } 29 | 30 | // GetActionApproval gets the ActionApproval for the provided name 31 | func (c *Client) GetActionApproval(namespace, name string) (*autv1alpha1.ActionApproval, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.autopilot.AutopilotV1alpha1().ActionApprovals(namespace).Get(name, metav1.GetOptions{}) 36 | } 37 | 38 | // UpdateActionApproval updates the ActionApproval 39 | func (c *Client) UpdateActionApproval(namespace string, actionApproval *autv1alpha1.ActionApproval) (*autv1alpha1.ActionApproval, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.autopilot.AutopilotV1alpha1().ActionApprovals(namespace).Update(actionApproval) 44 | } 45 | 46 | // DeleteActionApproval deletes the ActionApproval of the given name 47 | func (c *Client) DeleteActionApproval(namespace, name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.autopilot.AutopilotV1alpha1().ActionApprovals(namespace).Delete(name, &metav1.DeleteOptions{}) 52 | } 53 | 54 | // ListActionApprovals lists ActionApproval 55 | func (c *Client) ListActionApprovals(namespace string) (*autv1alpha1.ActionApprovalList, error) { 56 | if err := c.initClient(); err != nil { 57 | return nil, err 58 | } 59 | return c.autopilot.AutopilotV1alpha1().ActionApprovals(namespace).List(metav1.ListOptions{}) 60 | } 61 | -------------------------------------------------------------------------------- /k8s/autopilot/autopilot.go: -------------------------------------------------------------------------------- 1 | package autopilot 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | autopilotclientset "github.com/libopenstorage/autopilot-api/pkg/client/clientset/versioned" 9 | "github.com/portworx/sched-ops/k8s/common" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops provides an interface to Autopilot operations. 20 | type Ops interface { 21 | RuleOps 22 | RuleObjectOps 23 | ActionApprovalInterface 24 | // SetConfig sets the config and resets the client 25 | SetConfig(config *rest.Config) 26 | } 27 | 28 | // Instance returns a singleton instance of the client. 29 | func Instance() Ops { 30 | once.Do(func() { 31 | if instance == nil { 32 | instance = &Client{} 33 | } 34 | }) 35 | return instance 36 | } 37 | 38 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 39 | func SetInstance(i Ops) { 40 | instance = i 41 | } 42 | 43 | // New builds a new autopilot client. 44 | func New(c autopilotclientset.Interface) *Client { 45 | return &Client{ 46 | autopilot: c, 47 | } 48 | } 49 | 50 | // NewForConfig builds a new autopilot client for the given config. 51 | func NewForConfig(cfg *rest.Config) (*Client, error) { 52 | client, err := autopilotclientset.NewForConfig(cfg) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return &Client{ 58 | autopilot: client, 59 | }, nil 60 | } 61 | 62 | // NewInstanceFromConfigFile returns new instance of client by using given 63 | // config file 64 | func NewInstanceFromConfigFile(config string) (Ops, error) { 65 | newInstance := &Client{} 66 | err := newInstance.loadClientFromKubeconfig(config) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return newInstance, nil 71 | } 72 | 73 | // Client provides a wrapper for the autopilot client. 74 | type Client struct { 75 | config *rest.Config 76 | autopilot autopilotclientset.Interface 77 | } 78 | 79 | // SetConfig sets the config and resets the client. 80 | func (c *Client) SetConfig(cfg *rest.Config) { 81 | c.config = cfg 82 | c.autopilot = nil 83 | } 84 | 85 | // initClient the k8s client if uninitialized 86 | func (c *Client) initClient() error { 87 | if c.autopilot != nil { 88 | return nil 89 | } 90 | 91 | return c.setClient() 92 | } 93 | 94 | // setClient instantiates a client. 95 | func (c *Client) setClient() error { 96 | var err error 97 | 98 | if c.config != nil { 99 | err = c.loadClient() 100 | } else { 101 | kubeconfig := os.Getenv("KUBECONFIG") 102 | if len(kubeconfig) > 0 { 103 | err = c.loadClientFromKubeconfig(kubeconfig) 104 | } else { 105 | err = c.loadClientFromServiceAccount() 106 | } 107 | 108 | } 109 | 110 | return err 111 | } 112 | 113 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 114 | func (c *Client) loadClientFromServiceAccount() error { 115 | config, err := rest.InClusterConfig() 116 | if err != nil { 117 | return err 118 | } 119 | 120 | c.config = config 121 | return c.loadClient() 122 | } 123 | 124 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 125 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 126 | if err != nil { 127 | return err 128 | } 129 | 130 | c.config = config 131 | return c.loadClient() 132 | } 133 | 134 | func (c *Client) loadClient() error { 135 | if c.config == nil { 136 | return fmt.Errorf("rest config is not provided") 137 | } 138 | 139 | var err error 140 | err = common.SetRateLimiter(c.config) 141 | if err != nil { 142 | return err 143 | } 144 | c.autopilot, err = autopilotclientset.NewForConfig(c.config) 145 | if err != nil { 146 | return err 147 | } 148 | 149 | return nil 150 | } 151 | -------------------------------------------------------------------------------- /k8s/autopilot/autopilot_test.go: -------------------------------------------------------------------------------- 1 | package autopilot 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/autopilot/autopilotrule.go: -------------------------------------------------------------------------------- 1 | package autopilot 2 | 3 | import ( 4 | autv1alpha1 "github.com/libopenstorage/autopilot-api/pkg/apis/autopilot/v1alpha1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // RuleOps is an interface to perform k8s AutopilotRule operations 9 | type RuleOps interface { 10 | // CreateAutopilotRule creates the AutopilotRule object 11 | CreateAutopilotRule(*autv1alpha1.AutopilotRule) (*autv1alpha1.AutopilotRule, error) 12 | // GetAutopilotRule gets the AutopilotRule for the provided name 13 | GetAutopilotRule(string) (*autv1alpha1.AutopilotRule, error) 14 | // UpdateAutopilotRule updates the AutopilotRule 15 | UpdateAutopilotRule(*autv1alpha1.AutopilotRule) (*autv1alpha1.AutopilotRule, error) 16 | // DeleteAutopilotRule deletes the AutopilotRule of the given name 17 | DeleteAutopilotRule(string) error 18 | // ListAutopilotRules lists AutopilotRules 19 | ListAutopilotRules() (*autv1alpha1.AutopilotRuleList, error) 20 | } 21 | 22 | // CreateAutopilotRule creates the AutopilotRule object 23 | func (c *Client) CreateAutopilotRule(rule *autv1alpha1.AutopilotRule) (*autv1alpha1.AutopilotRule, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.autopilot.AutopilotV1alpha1().AutopilotRules().Create(rule) 28 | } 29 | 30 | // GetAutopilotRule gets the AutopilotRule for the provided name 31 | func (c *Client) GetAutopilotRule(name string) (*autv1alpha1.AutopilotRule, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.autopilot.AutopilotV1alpha1().AutopilotRules().Get(name, metav1.GetOptions{}) 36 | } 37 | 38 | // UpdateAutopilotRule updates the AutopilotRule 39 | func (c *Client) UpdateAutopilotRule(rule *autv1alpha1.AutopilotRule) (*autv1alpha1.AutopilotRule, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.autopilot.AutopilotV1alpha1().AutopilotRules().Update(rule) 44 | } 45 | 46 | // DeleteAutopilotRule deletes the AutopilotRule of the given name 47 | func (c *Client) DeleteAutopilotRule(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.autopilot.AutopilotV1alpha1().AutopilotRules().Delete(name, &metav1.DeleteOptions{}) 52 | } 53 | 54 | // ListAutopilotRules lists AutopilotRules 55 | func (c *Client) ListAutopilotRules() (*autv1alpha1.AutopilotRuleList, error) { 56 | if err := c.initClient(); err != nil { 57 | return nil, err 58 | } 59 | return c.autopilot.AutopilotV1alpha1().AutopilotRules().List(metav1.ListOptions{}) 60 | } 61 | -------------------------------------------------------------------------------- /k8s/autopilot/autopilotruleobject.go: -------------------------------------------------------------------------------- 1 | package autopilot 2 | 3 | import ( 4 | autv1alpha1 "github.com/libopenstorage/autopilot-api/pkg/apis/autopilot/v1alpha1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "k8s.io/apimachinery/pkg/watch" 7 | ) 8 | 9 | // RuleObjectOps is an interface to perform k8s AutopilotRuleObjects operations 10 | type RuleObjectOps interface { 11 | // CreateAutopilotRuleObject creates the AutopilotRuleObject object 12 | CreateAutopilotRuleObject(*autv1alpha1.AutopilotRuleObject) (*autv1alpha1.AutopilotRuleObject, error) 13 | // GetAutopilotRuleObject gets the AutopilotRuleObject for the provided name 14 | GetAutopilotRuleObject(namespace, name string) (*autv1alpha1.AutopilotRuleObject, error) 15 | // UpdateAutopilotRuleObject updates the AutopilotRuleObject 16 | UpdateAutopilotRuleObject(namespace string, object *autv1alpha1.AutopilotRuleObject) (*autv1alpha1.AutopilotRuleObject, error) 17 | // DeleteAutopilotRuleObject deletes the AutopilotRuleObject of the given name 18 | DeleteAutopilotRuleObject(namespace, name string) error 19 | // ListAutopilotRuleObjects lists AutopilotRulesObjects 20 | ListAutopilotRuleObjects(namespace string) (*autv1alpha1.AutopilotRuleObjectList, error) 21 | // WatchAutopilotRuleObjects watches AutopilotRulesObjects 22 | WatchAutopilotRuleObjects(namespace string, options metav1.ListOptions) (watch.Interface, error) 23 | } 24 | 25 | // CreateAutopilotRuleObject creates the AutopilotRuleObject object 26 | func (c *Client) CreateAutopilotRuleObject(ruleObject *autv1alpha1.AutopilotRuleObject) (*autv1alpha1.AutopilotRuleObject, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(ruleObject.Namespace).Create(ruleObject) 31 | } 32 | 33 | // GetAutopilotRuleObject gets the AutopilotRuleObject for the provided name 34 | func (c *Client) GetAutopilotRuleObject(namespace, name string) (*autv1alpha1.AutopilotRuleObject, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(namespace).Get(name, metav1.GetOptions{}) 39 | } 40 | 41 | // UpdateAutopilotRuleObject updates the AutopilotRuleObject 42 | func (c *Client) UpdateAutopilotRuleObject(namespace string, ruleObject *autv1alpha1.AutopilotRuleObject) (*autv1alpha1.AutopilotRuleObject, error) { 43 | if err := c.initClient(); err != nil { 44 | return nil, err 45 | } 46 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(namespace).Update(ruleObject) 47 | } 48 | 49 | // DeleteAutopilotRuleObject deletes the AutopilotRuleObject of the given name 50 | func (c *Client) DeleteAutopilotRuleObject(namespace, name string) error { 51 | if err := c.initClient(); err != nil { 52 | return err 53 | } 54 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(namespace).Delete(name, &metav1.DeleteOptions{}) 55 | } 56 | 57 | // ListAutopilotRuleObjects lists AutopilotRuleObjects 58 | func (c *Client) ListAutopilotRuleObjects(namespace string) (*autv1alpha1.AutopilotRuleObjectList, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(namespace).List(metav1.ListOptions{}) 63 | } 64 | 65 | // WatchAutopilotRuleObjects watches AutopilotRuleObjects 66 | func (c *Client) WatchAutopilotRuleObjects(namespace string, options metav1.ListOptions) (watch.Interface, error) { 67 | if err := c.initClient(); err != nil { 68 | return nil, err 69 | } 70 | return c.autopilot.AutopilotV1alpha1().AutopilotRuleObjects(namespace).Watch(options) 71 | } 72 | -------------------------------------------------------------------------------- /k8s/batch/batch_test.go: -------------------------------------------------------------------------------- 1 | package batch 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/batch/cron.go: -------------------------------------------------------------------------------- 1 | package batch 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | schederrors "github.com/portworx/sched-ops/k8s/errors" 9 | v1 "k8s.io/api/batch/v1" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | // CronOps is an interface to perform kubernetes related operations on the crd resources. 14 | type CronOps interface { 15 | // CreateCronJob creates the given cronJob 16 | CreateCronJob(cronJob *v1.CronJob) (*v1.CronJob, error) 17 | // UpdateCronJob updates the given cronJob 18 | UpdateCronJob(cronJob *v1.CronJob) (*v1.CronJob, error) 19 | // GetCronJob returns the cronJob given name and namespace 20 | GetCronJob(name, namespace string) (*v1.CronJob, error) 21 | // DeleteCronJob deletes the given cronJob 22 | DeleteCronJob(name, namespace string) error 23 | // ValidateCronJob validates the given cronJob 24 | ValidateCronJob(cronJob *v1.CronJob, timeout, retryInterval time.Duration) error 25 | // ListCronJobs list cronjobs in given namespace 26 | ListCronJobs(namespace string, filterOptions metav1.ListOptions) (*v1.CronJobList, error) 27 | } 28 | 29 | // NamespaceDefault is a default namespace for cronjob 30 | var NamespaceDefault = "default" 31 | 32 | // CreateCronJob creates the given cronJob 33 | func (c *Client) CreateCronJob(cronJob *v1.CronJob) (*v1.CronJob, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | 38 | ns := cronJob.Namespace 39 | if len(ns) == 0 { 40 | ns = NamespaceDefault 41 | } 42 | 43 | return c.batch.CronJobs(ns).Create(context.TODO(), cronJob, metav1.CreateOptions{}) 44 | } 45 | 46 | // UpdateCronJob updates the given cronJob 47 | func (c *Client) UpdateCronJob(cronJob *v1.CronJob) (*v1.CronJob, error) { 48 | if err := c.initClient(); err != nil { 49 | return nil, err 50 | } 51 | 52 | return c.batch.CronJobs(cronJob.Namespace).Update(context.TODO(), cronJob, metav1.UpdateOptions{}) 53 | } 54 | 55 | // GetCronJob returns the cronJob given name and namespace 56 | func (c *Client) GetCronJob(name, namespace string) (*v1.CronJob, error) { 57 | if err := c.initClient(); err != nil { 58 | return nil, err 59 | } 60 | 61 | return c.batch.CronJobs(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 62 | } 63 | 64 | // DeleteCronJob deletes the given cronJob 65 | func (c *Client) DeleteCronJob(name, namespace string) error { 66 | if err := c.initClient(); err != nil { 67 | return err 68 | } 69 | 70 | return c.batch.CronJobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 71 | } 72 | 73 | // ValidateCronJob validates the given cronJob 74 | func (c *Client) ValidateCronJob(cronJob *v1.CronJob, timeout, retryInterval time.Duration) error { 75 | if err := c.initClient(); err != nil { 76 | return err 77 | } 78 | 79 | result, err := c.batch.CronJobs(cronJob.Namespace).Get(context.TODO(), cronJob.Name, metav1.GetOptions{}) 80 | if result == nil { 81 | return err 82 | } 83 | if result.Status.LastScheduleTime.IsZero() { 84 | return &schederrors.ErrFailedToExecCronJob{ 85 | Name: result.Name, 86 | Cause: fmt.Sprintf("Cron job %s was not executed after %v", result.Name, timeout), 87 | } 88 | } 89 | return nil 90 | } 91 | 92 | // ListCronJobs returns the cronJobs in given namespace 93 | func (c *Client) ListCronJobs(namespace string, filterOptions metav1.ListOptions) (*v1.CronJobList, error) { 94 | if err := c.initClient(); err != nil { 95 | return nil, err 96 | } 97 | 98 | return c.batch.CronJobs(namespace).List(context.TODO(), filterOptions) 99 | } 100 | -------------------------------------------------------------------------------- /k8s/batch/cron_v1beta1.go: -------------------------------------------------------------------------------- 1 | package batch 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | schederrors "github.com/portworx/sched-ops/k8s/errors" 9 | v1beta1 "k8s.io/api/batch/v1beta1" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | // CronV1beta1Ops is an interface to perform kubernetes related operations on the cronjob resources. 14 | type CronV1beta1Ops interface { 15 | // CreateCronJobV1beta1 creates the given cronJob 16 | CreateCronJobV1beta1(cronJob *v1beta1.CronJob) (*v1beta1.CronJob, error) 17 | // UpdateCronJobV1beta1 updates the given cronJob 18 | UpdateCronJobV1beta1(cronJob *v1beta1.CronJob) (*v1beta1.CronJob, error) 19 | // GetCronJobV1beta1 returns the cronJob given name and namespace 20 | GetCronJobV1beta1(name, namespace string) (*v1beta1.CronJob, error) 21 | // DeleteCronJobV1beta1 deletes the given cronJob 22 | DeleteCronJobV1beta1(name, namespace string) error 23 | // ValidateCronJobV1beta1 validates the given cronJob 24 | ValidateCronJobV1beta1(cronJob *v1beta1.CronJob, timeout, retryInterval time.Duration) error 25 | // ListCronJobsV1beta1 list cronjobs in given namespace 26 | ListCronJobsV1beta1(namespace string, filterOptions metav1.ListOptions) (*v1beta1.CronJobList, error) 27 | } 28 | 29 | // CreateCronJobV1beta1 creates the given cronJob 30 | func (c *Client) CreateCronJobV1beta1(cronJob *v1beta1.CronJob) (*v1beta1.CronJob, error) { 31 | if err := c.initClient(); err != nil { 32 | return nil, err 33 | } 34 | 35 | ns := cronJob.Namespace 36 | if len(ns) == 0 { 37 | ns = NamespaceDefault 38 | } 39 | 40 | return c.batchv1beta1.CronJobs(ns).Create(context.TODO(), cronJob, metav1.CreateOptions{}) 41 | } 42 | 43 | // UpdateCronJobV1beta1 updates the given cronJob 44 | func (c *Client) UpdateCronJobV1beta1(cronJob *v1beta1.CronJob) (*v1beta1.CronJob, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | 49 | return c.batchv1beta1.CronJobs(cronJob.Namespace).Update(context.TODO(), cronJob, metav1.UpdateOptions{}) 50 | } 51 | 52 | // GetCronJobV1beta1 returns the cronJob given name and namespace 53 | func (c *Client) GetCronJobV1beta1(name, namespace string) (*v1beta1.CronJob, error) { 54 | if err := c.initClient(); err != nil { 55 | return nil, err 56 | } 57 | 58 | return c.batchv1beta1.CronJobs(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 59 | } 60 | 61 | // DeleteCronJobV1beta1 deletes the given cronJob 62 | func (c *Client) DeleteCronJobV1beta1(name, namespace string) error { 63 | if err := c.initClient(); err != nil { 64 | return err 65 | } 66 | 67 | return c.batchv1beta1.CronJobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 68 | } 69 | 70 | // ValidateCronJobV1beta1 validates the given cronJob 71 | func (c *Client) ValidateCronJobV1beta1(cronJob *v1beta1.CronJob, timeout, retryInterval time.Duration) error { 72 | if err := c.initClient(); err != nil { 73 | return err 74 | } 75 | 76 | result, err := c.batchv1beta1.CronJobs(cronJob.Namespace).Get(context.TODO(), cronJob.Name, metav1.GetOptions{}) 77 | if result == nil { 78 | return err 79 | } 80 | if result.Status.LastScheduleTime.IsZero() { 81 | return &schederrors.ErrFailedToExecCronJob{ 82 | Name: result.Name, 83 | Cause: fmt.Sprintf("Cron job %s was not executed after %v", result.Name, timeout), 84 | } 85 | } 86 | return nil 87 | } 88 | 89 | // ListCronJobsV1beta1 returns the cronJobs in given namespace 90 | func (c *Client) ListCronJobsV1beta1(namespace string, filterOptions metav1.ListOptions) (*v1beta1.CronJobList, error) { 91 | if err := c.initClient(); err != nil { 92 | return nil, err 93 | } 94 | 95 | return c.batchv1beta1.CronJobs(namespace).List(context.TODO(), filterOptions) 96 | } 97 | -------------------------------------------------------------------------------- /k8s/common/pvc.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | corev1 "k8s.io/api/core/v1" 8 | storagev1 "k8s.io/api/storage/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | v1 "k8s.io/client-go/kubernetes/typed/storage/v1" 11 | ) 12 | 13 | // IsPVCShared returns true if the PersistentVolumeClaim has been configured for use by multiple clients. 14 | func IsPVCShared(pvc *corev1.PersistentVolumeClaim) bool { 15 | for _, mode := range pvc.Spec.AccessModes { 16 | if mode == corev1.ReadOnlyMany || mode == corev1.ReadWriteMany { 17 | return true 18 | } 19 | } 20 | 21 | return false 22 | } 23 | 24 | // GetStorageClassForPVC tries to find a storage class by pvc spec definitions or by pvc annotations. 25 | func GetStorageClassForPVC(client v1.StorageV1Interface, pvc *corev1.PersistentVolumeClaim) (*storagev1.StorageClass, error) { 26 | var scName string 27 | if pvc.Spec.StorageClassName != nil && len(*pvc.Spec.StorageClassName) > 0 { 28 | scName = *pvc.Spec.StorageClassName 29 | } else { 30 | scName = pvc.Annotations[corev1.BetaStorageClassAnnotation] 31 | } 32 | 33 | if len(scName) == 0 { 34 | return nil, fmt.Errorf("PVC: %s does not have a storage class", pvc.Name) 35 | } 36 | 37 | return client.StorageClasses().Get(context.TODO(), scName, metav1.GetOptions{}) 38 | } 39 | -------------------------------------------------------------------------------- /k8s/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | 8 | "k8s.io/client-go/rest" 9 | ) 10 | 11 | const ( 12 | qPSRate = "KUBERNETES_OPS_QPS_RATE" 13 | burstRate = "KUBERNETES_OPS_BURST_RATE" 14 | ) 15 | 16 | // SetRateLimiter sets rate limiter 17 | func SetRateLimiter(config *rest.Config) error { 18 | if val := os.Getenv(qPSRate); val != "" { 19 | qps, err := strconv.Atoi(val) 20 | if err != nil { 21 | return fmt.Errorf("invalid qps count specified %v: %v", val, err) 22 | } 23 | config.QPS = float32(qps) 24 | } 25 | if val := os.Getenv(burstRate); val != "" { 26 | burst, err := strconv.Atoi(val) 27 | if err != nil { 28 | return fmt.Errorf("invalid burst count specified %v: %v", val, err) 29 | } 30 | config.Burst = int(burst) 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /k8s/core/configmap/configmap_test.go: -------------------------------------------------------------------------------- 1 | package configmap 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | 11 | coreops "github.com/portworx/sched-ops/k8s/core" 12 | "github.com/portworx/sched-ops/k8s/testutil" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | ) 15 | 16 | func TestGetConfigMap(t *testing.T) { 17 | setUpConfigMapTestCluster(t) 18 | 19 | configData := map[string]string{ 20 | "key1": "val1", 21 | } 22 | cm, err := New("px-configmaps-get-test", configData, testLockTimeout, 5, 0, 0) 23 | require.NoError(t, err, "Unexpected error in creating configmap") 24 | 25 | resultMap, err := cm.Get() 26 | require.NoError(t, err, "Unexpected error in getting configmap") 27 | require.Contains(t, resultMap, "key1") 28 | fmt.Println(resultMap) 29 | } 30 | 31 | func TestDeleteConfigMap(t *testing.T) { 32 | setUpConfigMapTestCluster(t) 33 | 34 | configData := map[string]string{ 35 | "key1": "val1", 36 | } 37 | 38 | cm, err := New("px-configmaps-delete-test", configData, testLockTimeout, 5, 0, 0) 39 | require.NoError(t, err, "Unexpected error in creating configmap") 40 | 41 | err = cm.Delete() 42 | require.NoError(t, err, "Unexpected error in delete") 43 | } 44 | 45 | func TestIncrementGeneration(t *testing.T) { 46 | setUpConfigMapTestCluster(t) 47 | 48 | configData := map[string]string{ 49 | "key1": "1", 50 | } 51 | cmIntf, err := New("px-configmaps-increment-generation-test", configData, testLockTimeout, 5, 0, 0) 52 | require.NoError(t, err, "Unexpected error in creating configmap") 53 | 54 | cm := cmIntf.(*configMap) 55 | 56 | rawCM, err := coreops.Instance().GetConfigMap(cm.name, k8sSystemNamespace) 57 | require.NoError(t, err, "Unexpected error in getting raw configmap") 58 | 59 | require.Equal(t, "", rawCM.Data[pxGenerationKey]) 60 | newGen := cm.incrementGeneration(rawCM) 61 | require.Equal(t, "1", rawCM.Data[pxGenerationKey]) 62 | require.Equal(t, uint64(1), newGen) 63 | 64 | newGen = cm.incrementGeneration(rawCM) 65 | require.Equal(t, "2", rawCM.Data[pxGenerationKey]) 66 | require.Equal(t, uint64(2), newGen) 67 | 68 | rawCM.Data[pxGenerationKey] = "123456789" 69 | newGen = cm.incrementGeneration(rawCM) 70 | require.Equal(t, "123456790", rawCM.Data[pxGenerationKey]) 71 | require.Equal(t, uint64(123456790), newGen) 72 | 73 | rawCM.Data[pxGenerationKey] = "invalid" 74 | newGen = cm.incrementGeneration(rawCM) 75 | require.Equal(t, "1", rawCM.Data[pxGenerationKey]) 76 | require.Equal(t, uint64(1), newGen) 77 | 78 | // max uint64 79 | rawCM.Data[pxGenerationKey] = "18446744073709551615" 80 | newGen = cm.incrementGeneration(rawCM) 81 | require.Equal(t, "1", rawCM.Data[pxGenerationKey]) 82 | require.Equal(t, uint64(1), newGen) 83 | 84 | err = cm.Delete() 85 | require.NoError(t, err, "Unexpected error in delete") 86 | } 87 | 88 | func setUpConfigMapTestCluster(t *testing.T) { 89 | os.Setenv("KUBERNETES_OPS_QPS_RATE", "2000") 90 | os.Setenv("KUBERNETES_OPS_BURST_RATE", "4000") 91 | 92 | restCfg := testutil.SetUpTestCluster(t, "configmap-test-cluster") 93 | 94 | testClient, err := coreops.NewForConfig(restCfg) 95 | require.NoError(t, err) 96 | 97 | coreops.SetInstance(testClient) 98 | // delete all the test configmaps 99 | result, err := coreops.Instance().ListConfigMap(k8sSystemNamespace, metav1.ListOptions{}) 100 | require.NoError(t, err) 101 | for _, cm := range result.Items { 102 | if strings.Contains(cm.Name, "px-configmaps") && strings.Contains(cm.Name, "test") { 103 | t.Logf("Deleting configmap: %s/%s", k8sSystemNamespace, cm.Name) 104 | err = coreops.Instance().DeleteConfigMap(cm.Name, k8sSystemNamespace) 105 | require.NoError(t, err) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /k8s/core/core_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/core/endpoints.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/types" 9 | ) 10 | 11 | // EndpointsOps is an interface to deal with kubernetes endpoints. 12 | type EndpointsOps interface { 13 | // CreateEndpoints creates a given endpoints. 14 | CreateEndpoints(endpoints *corev1.Endpoints) (*corev1.Endpoints, error) 15 | // GetEndpoints retrieves endpoints for a given namespace/name. 16 | GetEndpoints(name, namespace string) (*corev1.Endpoints, error) 17 | // ListEndpoints retrieves endpoints for a given namespace 18 | ListEndpoints(string, metav1.ListOptions) (*corev1.EndpointsList, error) 19 | // PatchEndpoints applies a patch for a given endpoints. 20 | PatchEndpoints(name, namespace string, pt types.PatchType, jsonPatch []byte, subresources ...string) (*corev1.Endpoints, error) 21 | // DeleteEndpoints removes endpoints for a given namespace/name. 22 | DeleteEndpoints(name, namespace string) error 23 | // UpdateEndpoints updates the given endpoint 24 | UpdateEndpoints(endpoints *corev1.Endpoints) (*corev1.Endpoints, error) 25 | } 26 | 27 | // CreateEndpoints creates a given endpoints. 28 | func (c *Client) CreateEndpoints(endpoints *corev1.Endpoints) (*corev1.Endpoints, error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | return c.kubernetes.CoreV1().Endpoints(endpoints.Namespace).Create(context.TODO(), endpoints, metav1.CreateOptions{}) 33 | } 34 | 35 | // GetEndpoints retrieves endpoints for a given namespace/name. 36 | func (c *Client) GetEndpoints(name, ns string) (*corev1.Endpoints, error) { 37 | if err := c.initClient(); err != nil { 38 | return nil, err 39 | } 40 | return c.kubernetes.CoreV1().Endpoints(ns).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // ListEndpoints retrieves endpoints for a given namespace 44 | func (c *Client) ListEndpoints(ns string, opts metav1.ListOptions) (*corev1.EndpointsList, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | return c.kubernetes.CoreV1().Endpoints(ns).List(context.TODO(), opts) 49 | } 50 | 51 | // PatchEndpoints applies a patch for a given endpoints. 52 | func (c *Client) PatchEndpoints(name, ns string, pt types.PatchType, jsonPatch []byte, subresources ...string) (*corev1.Endpoints, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | return c.kubernetes.CoreV1().Endpoints(ns).Patch(context.TODO(), name, pt, jsonPatch, metav1.PatchOptions{}, subresources...) 57 | } 58 | 59 | // DeleteEndpoints retrieves endpoints for a given namespace/name. 60 | func (c *Client) DeleteEndpoints(name, ns string) error { 61 | if err := c.initClient(); err != nil { 62 | return err 63 | } 64 | return c.kubernetes.CoreV1().Endpoints(ns).Delete(context.TODO(), name, metav1.DeleteOptions{}) 65 | } 66 | 67 | // UpdateEndpoints updates the given endpoint. 68 | func (c *Client) UpdateEndpoints(endpoints *corev1.Endpoints) (*corev1.Endpoints, error) { 69 | if err := c.initClient(); err != nil { 70 | return nil, err 71 | } 72 | return c.kubernetes.CoreV1().Endpoints(endpoints.Namespace).Update(context.TODO(), endpoints, metav1.UpdateOptions{}) 73 | } 74 | -------------------------------------------------------------------------------- /k8s/core/limitrange.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/fields" 9 | ) 10 | 11 | // LimitRangeOps is an interface to perform k8s LimitRange operations 12 | type LimitRangeOps interface { 13 | // GetLimitRange gets the limitranges object given its name and namespace 14 | GetLimitRange(name string, namespace string) (*corev1.LimitRange, error) 15 | // CreateLimitRange creates the given limitrange 16 | CreateLimitRange(*corev1.LimitRange) (*corev1.LimitRange, error) 17 | // ListLimitRange changes and callback fn 18 | ListLimitRange(string, metav1.ListOptions) (*corev1.LimitRangeList, error) 19 | // UpdateLimitRange updates the given limitrange 20 | UpdateLimitRange(*corev1.LimitRange) (*corev1.LimitRange, error) 21 | // DeleteLimitRange deletes the given limitrange 22 | DeleteLimitRange(name, namespace string) error 23 | // WatchLimitRange changes and callback fn 24 | WatchLimitRange(*corev1.LimitRange, WatchFunc) error 25 | } 26 | 27 | // GetLimitRange gets the limitranges object given its name and namespace 28 | func (c *Client) GetLimitRange(name string, namespace string) (*corev1.LimitRange, error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | 33 | return c.kubernetes.CoreV1().LimitRanges(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 34 | } 35 | 36 | // CreateLimitRange creates the given limitrange 37 | func (c *Client) CreateLimitRange(limitrange *corev1.LimitRange) (*corev1.LimitRange, error) { 38 | if err := c.initClient(); err != nil { 39 | return nil, err 40 | } 41 | 42 | return c.kubernetes.CoreV1().LimitRanges(limitrange.Namespace).Create(context.TODO(), limitrange, metav1.CreateOptions{}) 43 | } 44 | 45 | // ListLimitRange creates the given limitrange 46 | func (c *Client) ListLimitRange(namespace string, opts metav1.ListOptions) (*corev1.LimitRangeList, error) { 47 | if err := c.initClient(); err != nil { 48 | return nil, err 49 | } 50 | 51 | return c.kubernetes.CoreV1().LimitRanges(namespace).List(context.TODO(), opts) 52 | } 53 | 54 | // UpdateLimitRange updates the given limitrange 55 | func (c *Client) UpdateLimitRange(limitrange *corev1.LimitRange) (*corev1.LimitRange, error) { 56 | if err := c.initClient(); err != nil { 57 | return nil, err 58 | } 59 | 60 | return c.kubernetes.CoreV1().LimitRanges(limitrange.Namespace).Update(context.TODO(), limitrange, metav1.UpdateOptions{}) 61 | } 62 | 63 | // DeleteLimitRange deletes the given limitrange 64 | func (c *Client) DeleteLimitRange(name, namespace string) error { 65 | if err := c.initClient(); err != nil { 66 | return err 67 | } 68 | 69 | return c.kubernetes.CoreV1().LimitRanges(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 70 | PropagationPolicy: &deleteForegroundPolicy, 71 | }) 72 | } 73 | 74 | // WatchLimitRange changes and callback fn 75 | func (c *Client) WatchLimitRange(limitrange *corev1.LimitRange, fn WatchFunc) error { 76 | if err := c.initClient(); err != nil { 77 | return err 78 | } 79 | 80 | listOptions := metav1.ListOptions{ 81 | FieldSelector: fields.OneTermEqualSelector("metadata.name", limitrange.Name).String(), 82 | Watch: true, 83 | } 84 | 85 | watchInterface, err := c.kubernetes.CoreV1().LimitRanges(limitrange.Namespace).Watch(context.TODO(), listOptions) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | // fire off watch function 91 | go c.handleWatch(watchInterface, limitrange, "", fn, listOptions) 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /k8s/core/networkpolicy.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/networking/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/types" 9 | ) 10 | 11 | // NetworkPolicyOps is an interface to deal with kubernetes NetworkPolicy. 12 | type NetworkPolicyOps interface { 13 | // CreateNetworkPolicy creates a given network policy. 14 | CreateNetworkPolicy(NetworkPolicy *v1.NetworkPolicy) (*v1.NetworkPolicy, error) 15 | // GetNetworkPolicy retrieves NetworkPolicy for a given namespace/name. 16 | GetNetworkPolicy(name, namespace string) (*v1.NetworkPolicy, error) 17 | // ListNetworkPolicy retrieves NetworkPolicy for a given namespace/name. 18 | ListNetworkPolicy(namespace string, listOptions metav1.ListOptions) (*v1.NetworkPolicyList, error) 19 | // PatchNetworkPolicy applies a patch for a given NetworkPolicy. 20 | PatchNetworkPolicy(name, namespace string, pt types.PatchType, jsonPatch []byte, subresources ...string) (*v1.NetworkPolicy, error) 21 | // DeleteNetworkPolicy removes NetworkPolicy for a given namespace/name. 22 | DeleteNetworkPolicy(name, namespace string) error 23 | // UpdateNetworkPolicy updates the given networkpolicy 24 | UpdateNetworkPolicy(NetworkPolicy *v1.NetworkPolicy) (*v1.NetworkPolicy, error) 25 | } 26 | 27 | // CreateNetworkPolicy creates a given NetworkPolicy. 28 | func (c *Client) CreateNetworkPolicy(NetworkPolicy *v1.NetworkPolicy) (*v1.NetworkPolicy, error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | return c.kubernetes.NetworkingV1().NetworkPolicies(NetworkPolicy.Namespace).Create(context.TODO(), NetworkPolicy, metav1.CreateOptions{}) 33 | } 34 | 35 | // GetNetworkPolicy retrieves NetworkPolicy for a given namespace/name. 36 | func (c *Client) GetNetworkPolicy(name, ns string) (*v1.NetworkPolicy, error) { 37 | if err := c.initClient(); err != nil { 38 | return nil, err 39 | } 40 | return c.kubernetes.NetworkingV1().NetworkPolicies(ns).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // ListNetworkPolicy retrieves NetworkPolicies for a given namespace. 44 | func (c *Client) ListNetworkPolicy(ns string, opts metav1.ListOptions) (*v1.NetworkPolicyList, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | return c.kubernetes.NetworkingV1().NetworkPolicies(ns).List(context.TODO(), opts) 49 | } 50 | 51 | // PatchNetworkPolicy applies a patch for a given NetworkPolicy. 52 | func (c *Client) PatchNetworkPolicy(name, ns string, pt types.PatchType, jsonPatch []byte, subresources ...string) (*v1.NetworkPolicy, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | return c.kubernetes.NetworkingV1().NetworkPolicies(ns).Patch(context.TODO(), name, pt, jsonPatch, metav1.PatchOptions{}, subresources...) 57 | } 58 | 59 | // DeleteNetworkPolicy retrieves NetworkPolicy for a given namespace/name. 60 | func (c *Client) DeleteNetworkPolicy(name, ns string) error { 61 | if err := c.initClient(); err != nil { 62 | return err 63 | } 64 | return c.kubernetes.NetworkingV1().NetworkPolicies(ns).Delete(context.TODO(), name, metav1.DeleteOptions{}) 65 | } 66 | 67 | // UpdateNetworkPolicy updates the given network policy. 68 | func (c *Client) UpdateNetworkPolicy(NetworkPolicy *v1.NetworkPolicy) (*v1.NetworkPolicy, error) { 69 | if err := c.initClient(); err != nil { 70 | return nil, err 71 | } 72 | return c.kubernetes.NetworkingV1().NetworkPolicies(NetworkPolicy.Namespace).Update(context.TODO(), NetworkPolicy, metav1.UpdateOptions{}) 73 | } 74 | -------------------------------------------------------------------------------- /k8s/core/persistenvolumeclaims_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | corev1 "k8s.io/api/core/v1" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | func TestGetPersistentVolumeClaimsV2(t *testing.T) { 14 | // Create a mock Client 15 | client := MockClient() 16 | SetInstance(client) 17 | 18 | // Add PVC to the fake clientset 19 | _, err := client.kubernetes.CoreV1().PersistentVolumeClaims("test-namespace-1").Create(context.TODO(), &corev1.PersistentVolumeClaim{ 20 | ObjectMeta: metav1.ObjectMeta{ 21 | Name: "test-pvc-1", 22 | Namespace: "test-namespace-1", 23 | Labels: map[string]string{"app": "test-app", "env": "test"}, 24 | }, 25 | }, metav1.CreateOptions{}) 26 | require.NoError(t, err) 27 | 28 | _, err = client.kubernetes.CoreV1().PersistentVolumeClaims("test-namespace-1").Create(context.TODO(), &corev1.PersistentVolumeClaim{ 29 | ObjectMeta: metav1.ObjectMeta{ 30 | Name: "test-pvc-2", 31 | Namespace: "test-namespace-1", 32 | Labels: map[string]string{"app": "test-app", "foo": "bar"}, 33 | }, 34 | }, metav1.CreateOptions{}) 35 | require.NoError(t, err) 36 | 37 | // Test 1 38 | labelSelector := metav1.LabelSelector{ 39 | MatchLabels: map[string]string{ 40 | "app": "test-app", 41 | }, 42 | } 43 | pvcList, err := client.GetPersistentVolumeClaimsUsingLabelSelector("test-namespace-1", labelSelector) 44 | require.NoError(t, err) 45 | assert.Len(t, pvcList.Items, 2) 46 | 47 | // Test 2 48 | labelSelector = metav1.LabelSelector{ 49 | MatchLabels: map[string]string{ 50 | "app": "test-app", 51 | }, 52 | MatchExpressions: []metav1.LabelSelectorRequirement{ 53 | { 54 | Key: "env", 55 | Operator: metav1.LabelSelectorOpIn, 56 | Values: []string{"test"}, 57 | }, 58 | }, 59 | } 60 | pvcList, err = client.GetPersistentVolumeClaimsUsingLabelSelector("test-namespace-1", labelSelector) 61 | require.NoError(t, err) 62 | assert.Len(t, pvcList.Items, 1) 63 | assert.Equal(t, "test-pvc-1", pvcList.Items[0].Name) 64 | 65 | // Test 3 66 | labelSelector = metav1.LabelSelector{ 67 | MatchExpressions: []metav1.LabelSelectorRequirement{ 68 | { 69 | Key: "foo", 70 | Operator: metav1.LabelSelectorOpDoesNotExist, 71 | }, 72 | }, 73 | } 74 | pvcList, err = client.GetPersistentVolumeClaimsUsingLabelSelector("test-namespace-1", labelSelector) 75 | require.NoError(t, err) 76 | assert.Len(t, pvcList.Items, 1) 77 | assert.Equal(t, "test-pvc-1", pvcList.Items[0].Name) 78 | } 79 | -------------------------------------------------------------------------------- /k8s/core/serviceaccounts.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | 6 | authenticationv1 "k8s.io/api/authentication/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // ServiceAccountOps is an interface to perform operations on role resources. 12 | type ServiceAccountOps interface { 13 | // CreateServiceAccount creates the given service account 14 | CreateServiceAccount(account *corev1.ServiceAccount) (*corev1.ServiceAccount, error) 15 | // GetServiceAccount gets the given service account 16 | GetServiceAccount(name, namespace string) (*corev1.ServiceAccount, error) 17 | // UpdateServiceAccount updates the given service account 18 | UpdateServiceAccount(account *corev1.ServiceAccount) (*corev1.ServiceAccount, error) 19 | // DeleteServiceAccount deletes the given service account 20 | DeleteServiceAccount(accountName, namespace string) error 21 | // ListServiceAccount in given namespace 22 | ListServiceAccount(namespace string, opts metav1.ListOptions) (*corev1.ServiceAccountList, error) 23 | // CreateToken creates a token associated with a serviceaccount through a tokenRequest 24 | CreateToken(name, namespace string, tokenRequest *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) 25 | } 26 | 27 | // CreateServiceAccount creates the given service account 28 | func (c *Client) CreateServiceAccount(account *corev1.ServiceAccount) (*corev1.ServiceAccount, error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | 33 | return c.kubernetes.CoreV1().ServiceAccounts(account.Namespace).Create(context.TODO(), account, metav1.CreateOptions{}) 34 | } 35 | 36 | // GetServiceAccount gets the given service account 37 | func (c *Client) GetServiceAccount(name, namespace string) (*corev1.ServiceAccount, error) { 38 | if err := c.initClient(); err != nil { 39 | return nil, err 40 | } 41 | 42 | return c.kubernetes.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 43 | } 44 | 45 | // ListServiceAccount in given namespace 46 | func (c *Client) ListServiceAccount(namespace string, opts metav1.ListOptions) (*corev1.ServiceAccountList, error) { 47 | if err := c.initClient(); err != nil { 48 | return nil, err 49 | } 50 | 51 | return c.kubernetes.CoreV1().ServiceAccounts(namespace).List(context.TODO(), opts) 52 | } 53 | 54 | // UpdateServiceAccount updates the given service account 55 | func (c *Client) UpdateServiceAccount(account *corev1.ServiceAccount) (*corev1.ServiceAccount, error) { 56 | if err := c.initClient(); err != nil { 57 | return nil, err 58 | } 59 | 60 | return c.kubernetes.CoreV1().ServiceAccounts(account.Namespace).Update(context.TODO(), account, metav1.UpdateOptions{}) 61 | } 62 | 63 | // DeleteServiceAccount deletes the given service account 64 | func (c *Client) DeleteServiceAccount(accountName, namespace string) error { 65 | if err := c.initClient(); err != nil { 66 | return err 67 | } 68 | 69 | return c.kubernetes.CoreV1().ServiceAccounts(namespace).Delete(context.TODO(), accountName, metav1.DeleteOptions{ 70 | PropagationPolicy: &deleteForegroundPolicy, 71 | }) 72 | } 73 | 74 | // CreateToken creates the server's representation of the tokenRequest associated with a service account 75 | func (c *Client) CreateToken(name, namespace string, tokenRequest *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) { 76 | if err := c.initClient(); err != nil { 77 | return nil, err 78 | } 79 | 80 | return c.kubernetes.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), name, tokenRequest, metav1.CreateOptions{}) 81 | } 82 | -------------------------------------------------------------------------------- /k8s/core/util.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "net" 5 | "os" 6 | "strings" 7 | 8 | "github.com/sirupsen/logrus" 9 | "k8s.io/apimachinery/pkg/labels" 10 | ) 11 | 12 | func mapToCSV(in map[string]string) string { 13 | if len(in) == 0 { 14 | return "" 15 | } 16 | return labels.FormatLabels(in) 17 | } 18 | 19 | func roundUpSize(volumeSizeBytes int64, allocationUnitBytes int64) int64 { 20 | return (volumeSizeBytes + allocationUnitBytes - 1) / allocationUnitBytes 21 | } 22 | 23 | // getLocalIPList returns the list of local IP addresses, and optionally includes local hostname. 24 | func getLocalIPList(includeHostname bool) ([]string, error) { 25 | ifaces, err := net.Interfaces() 26 | if err != nil { 27 | return nil, err 28 | } 29 | ipList := make([]string, 0, len(ifaces)) 30 | for _, i := range ifaces { 31 | addrs, err := i.Addrs() 32 | if err != nil { 33 | logrus.WithError(err).Warnf("Error listing address for %s (cont.)", i.Name) 34 | continue 35 | } 36 | for _, addr := range addrs { 37 | var ip net.IP 38 | switch v := addr.(type) { 39 | case *net.IPNet: 40 | ip = v.IP 41 | case *net.IPAddr: 42 | ip = v.IP 43 | } 44 | // process IP address 45 | if ip != nil && !ip.IsLoopback() && !ip.IsUnspecified() { 46 | ipList = append(ipList, ip.String()) 47 | } 48 | } 49 | } 50 | 51 | if includeHostname { 52 | hn, err := os.Hostname() 53 | if err == nil && hn != "" && !strings.HasPrefix(hn, "localhost") { 54 | ipList = append(ipList, hn) 55 | } 56 | } 57 | 58 | return ipList, nil 59 | } 60 | -------------------------------------------------------------------------------- /k8s/doc.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | -------------------------------------------------------------------------------- /k8s/dynamic/dynamic_test.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/externalsnapshotter/externalsnapshotter.go: -------------------------------------------------------------------------------- 1 | package externalsnapshotter 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1" 9 | "github.com/portworx/sched-ops/k8s/common" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops is an interface to perform operations on external storage resources. 20 | type Ops interface { 21 | SnapshotOps 22 | SnapshotContentOps 23 | SnapshotClassOps 24 | 25 | // SetConfig sets the config and resets the client 26 | SetConfig(config *rest.Config) 27 | } 28 | 29 | // Instance returns a singleton instance of the client. 30 | func Instance() Ops { 31 | once.Do(func() { 32 | if instance == nil { 33 | instance = &Client{} 34 | } 35 | }) 36 | return instance 37 | } 38 | 39 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 40 | func SetInstance(i Ops) { 41 | instance = i 42 | } 43 | 44 | // New builds a new client for the given config. 45 | func New(client v1.SnapshotV1Interface) *Client { 46 | return &Client{ 47 | client: client, 48 | } 49 | } 50 | 51 | // NewForConfig builds a new client for the given config. 52 | func NewForConfig(c *rest.Config) (*Client, error) { 53 | client, err := v1.NewForConfig(c) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return &Client{ 59 | client: client, 60 | }, nil 61 | } 62 | 63 | // NewInstanceFromConfigFile returns new instance of client by using given 64 | // config file 65 | func NewInstanceFromConfigFile(config string) (Ops, error) { 66 | newInstance := &Client{} 67 | err := newInstance.loadClientFromKubeconfig(config) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return newInstance, nil 72 | } 73 | 74 | // Client is a wrapper for the external-storage client. 75 | type Client struct { 76 | config *rest.Config 77 | 78 | client v1.SnapshotV1Interface 79 | } 80 | 81 | // SetConfig sets the config and resets the client 82 | func (c *Client) SetConfig(cfg *rest.Config) { 83 | c.config = cfg 84 | c.client = nil 85 | } 86 | 87 | // initClient the k8s client if uninitialized 88 | func (c *Client) initClient() error { 89 | if c.client != nil { 90 | return nil 91 | } 92 | 93 | return c.setClient() 94 | } 95 | 96 | // setClient instantiates a client. 97 | func (c *Client) setClient() error { 98 | var err error 99 | 100 | if c.config != nil { 101 | err = c.loadClient() 102 | } else { 103 | kubeconfig := os.Getenv("KUBECONFIG") 104 | if len(kubeconfig) > 0 { 105 | err = c.loadClientFromKubeconfig(kubeconfig) 106 | } else { 107 | err = c.loadClientFromServiceAccount() 108 | } 109 | 110 | } 111 | 112 | return err 113 | } 114 | 115 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 116 | func (c *Client) loadClientFromServiceAccount() error { 117 | config, err := rest.InClusterConfig() 118 | if err != nil { 119 | return err 120 | } 121 | 122 | c.config = config 123 | return c.loadClient() 124 | } 125 | 126 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 127 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | c.config = config 133 | return c.loadClient() 134 | } 135 | 136 | func (c *Client) loadClient() error { 137 | if c.config == nil { 138 | return fmt.Errorf("rest config is not provided") 139 | } 140 | 141 | var err error 142 | err = common.SetRateLimiter(c.config) 143 | if err != nil { 144 | return err 145 | } 146 | c.client, err = v1.NewForConfig(c.config) 147 | if err != nil { 148 | return err 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /k8s/externalsnapshotter/externalsnapshotter_test.go: -------------------------------------------------------------------------------- 1 | package externalsnapshotter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/externalsnapshotter/volumesnapshotclasses.go: -------------------------------------------------------------------------------- 1 | package externalsnapshotter 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // SnapshotClassOps is an interface to perform k8s VolumeSnapshotClass operations 11 | type SnapshotClassOps interface { 12 | // CreateSnapshotClass creates the given snapshot class 13 | CreateSnapshotClass(snap *v1.VolumeSnapshotClass) (*v1.VolumeSnapshotClass, error) 14 | // GetSnapshotClass returns the snapshot class for given name 15 | GetSnapshotClass(name string) (*v1.VolumeSnapshotClass, error) 16 | // ListSnapshotClasses lists all snapshot classes 17 | ListSnapshotClasses() (*v1.VolumeSnapshotClassList, error) 18 | // UpdateSnapshotClass updates the given snapshot class 19 | UpdateSnapshotClass(snap *v1.VolumeSnapshotClass) (*v1.VolumeSnapshotClass, error) 20 | // DeleteSnapshotClass deletes the given snapshot class 21 | DeleteSnapshotClass(name string) error 22 | } 23 | 24 | // CreateSnapshotClass creates the given snapshot class. 25 | func (c *Client) CreateSnapshotClass(snap *v1.VolumeSnapshotClass) (*v1.VolumeSnapshotClass, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.client.VolumeSnapshotClasses().Create(context.TODO(), snap, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetSnapshotClass returns the snapshot class for given name 33 | func (c *Client) GetSnapshotClass(name string) (*v1.VolumeSnapshotClass, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.client.VolumeSnapshotClasses().Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListSnapshotClasses lists all snapshot classes 41 | func (c *Client) ListSnapshotClasses() (*v1.VolumeSnapshotClassList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.client.VolumeSnapshotClasses().List(context.TODO(), metav1.ListOptions{}) 46 | } 47 | 48 | // UpdateSnapshotClass updates the given snapshot class 49 | func (c *Client) UpdateSnapshotClass(snap *v1.VolumeSnapshotClass) (*v1.VolumeSnapshotClass, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.client.VolumeSnapshotClasses().Update(context.TODO(), snap, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteSnapshotClass deletes the given snapshot 57 | func (c *Client) DeleteSnapshotClass(name string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.client.VolumeSnapshotClasses().Delete(context.TODO(), name, metav1.DeleteOptions{}) 62 | } 63 | -------------------------------------------------------------------------------- /k8s/externalsnapshotter/volumesnapshotcontents.go: -------------------------------------------------------------------------------- 1 | package externalsnapshotter 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // SnapshotContentOps is an interface to perform k8s VolumeSnapshotContent operations 11 | type SnapshotContentOps interface { 12 | // CreateSnapshotContent creates the given snapshot content 13 | CreateSnapshotContent(snap *v1.VolumeSnapshotContent) (*v1.VolumeSnapshotContent, error) 14 | // GetSnapshotContent returns the snapshot content for given name 15 | GetSnapshotContent(name string) (*v1.VolumeSnapshotContent, error) 16 | // ListSnapshotContents lists all snapshot contents 17 | ListSnapshotContents() (*v1.VolumeSnapshotContentList, error) 18 | // UpdateSnapshotContent updates the given snapshot content 19 | UpdateSnapshotContent(snap *v1.VolumeSnapshotContent) (*v1.VolumeSnapshotContent, error) 20 | // DeleteSnapshotContent deletes the given snapshot content 21 | DeleteSnapshotContent(name string) error 22 | } 23 | 24 | // CreateSnapshotContent creates the given snapshot content. 25 | func (c *Client) CreateSnapshotContent(snap *v1.VolumeSnapshotContent) (*v1.VolumeSnapshotContent, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.client.VolumeSnapshotContents().Create(context.TODO(), snap, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetSnapshotContent returns the snapshot content for given name 33 | func (c *Client) GetSnapshotContent(name string) (*v1.VolumeSnapshotContent, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.client.VolumeSnapshotContents().Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListSnapshotContents lists all snapshot contents 41 | func (c *Client) ListSnapshotContents() (*v1.VolumeSnapshotContentList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.client.VolumeSnapshotContents().List(context.TODO(), metav1.ListOptions{}) 46 | } 47 | 48 | // UpdateSnapshotContent updates the given snapshot content 49 | func (c *Client) UpdateSnapshotContent(snap *v1.VolumeSnapshotContent) (*v1.VolumeSnapshotContent, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.client.VolumeSnapshotContents().Update(context.TODO(), snap, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteSnapshotContent deletes the given snapshot content 57 | func (c *Client) DeleteSnapshotContent(name string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.client.VolumeSnapshotContents().Delete(context.TODO(), name, metav1.DeleteOptions{}) 62 | } 63 | -------------------------------------------------------------------------------- /k8s/externalsnapshotter/volumesnapshots.go: -------------------------------------------------------------------------------- 1 | package externalsnapshotter 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // SnapshotOps is an interface to perform k8s VolumeSnapshot operations 11 | type SnapshotOps interface { 12 | // CreateSnapshot creates the given snapshot 13 | CreateSnapshot(snap *v1.VolumeSnapshot) (*v1.VolumeSnapshot, error) 14 | // GetSnapshot returns the snapshot for given name and namespace 15 | GetSnapshot(name string, namespace string) (*v1.VolumeSnapshot, error) 16 | // ListSnapshots lists all snapshots in the given namespace 17 | ListSnapshots(namespace string) (*v1.VolumeSnapshotList, error) 18 | // UpdateSnapshot updates the given snapshot 19 | UpdateSnapshot(snap *v1.VolumeSnapshot) (*v1.VolumeSnapshot, error) 20 | // DeleteSnapshot deletes the given snapshot 21 | DeleteSnapshot(name string, namespace string) error 22 | } 23 | 24 | // CreateSnapshot creates the given snapshot. 25 | func (c *Client) CreateSnapshot(snap *v1.VolumeSnapshot) (*v1.VolumeSnapshot, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.client.VolumeSnapshots(snap.Namespace).Create(context.TODO(), snap, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetSnapshot returns the snapshot for given name and namespace 33 | func (c *Client) GetSnapshot(name string, namespace string) (*v1.VolumeSnapshot, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.client.VolumeSnapshots(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListSnapshots lists all snapshots in the given namespace 41 | func (c *Client) ListSnapshots(namespace string) (*v1.VolumeSnapshotList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.client.VolumeSnapshots(namespace).List(context.TODO(), metav1.ListOptions{}) 46 | } 47 | 48 | // UpdateSnapshot updates the given snapshot 49 | func (c *Client) UpdateSnapshot(snap *v1.VolumeSnapshot) (*v1.VolumeSnapshot, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.client.VolumeSnapshots(snap.Namespace).Update(context.TODO(), snap, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteSnapshot deletes the given snapshot 57 | func (c *Client) DeleteSnapshot(name string, namespace string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.client.VolumeSnapshots(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 62 | } 63 | -------------------------------------------------------------------------------- /k8s/externalstorage/externalstorage.go: -------------------------------------------------------------------------------- 1 | package externalstorage 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | snapclient "github.com/kubernetes-incubator/external-storage/snapshot/pkg/client" 9 | "github.com/portworx/sched-ops/k8s/common" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops is an interface to perform operations on external storage resources. 20 | type Ops interface { 21 | SnapshotOps 22 | 23 | // SetConfig sets the config and resets the client 24 | SetConfig(config *rest.Config) 25 | } 26 | 27 | // Instance returns a singleton instance of the client. 28 | func Instance() Ops { 29 | once.Do(func() { 30 | if instance == nil { 31 | instance = &Client{} 32 | } 33 | }) 34 | return instance 35 | } 36 | 37 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 38 | func SetInstance(i Ops) { 39 | instance = i 40 | } 41 | 42 | // New builds a new client for the given config. 43 | func New(client rest.Interface) *Client { 44 | return &Client{ 45 | snap: client, 46 | } 47 | } 48 | 49 | // NewForConfig builds a new client for the given config. 50 | func NewForConfig(c *rest.Config) (*Client, error) { 51 | snap, _, err := snapclient.NewClient(c) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return &Client{ 57 | snap: snap, 58 | }, nil 59 | } 60 | 61 | // NewInstanceFromConfigFile returns new instance of client by using given 62 | // config file 63 | func NewInstanceFromConfigFile(config string) (Ops, error) { 64 | newInstance := &Client{} 65 | err := newInstance.loadClientFromKubeconfig(config) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return newInstance, nil 70 | } 71 | 72 | // Client is a wrapper for the external-storage client. 73 | type Client struct { 74 | config *rest.Config 75 | snap rest.Interface 76 | } 77 | 78 | // SetConfig sets the config and resets the client 79 | func (c *Client) SetConfig(cfg *rest.Config) { 80 | c.config = cfg 81 | c.snap = nil 82 | } 83 | 84 | // initClient the k8s client if uninitialized 85 | func (c *Client) initClient() error { 86 | if c.snap != nil { 87 | return nil 88 | } 89 | 90 | return c.setClient() 91 | } 92 | 93 | // setClient instantiates a client. 94 | func (c *Client) setClient() error { 95 | var err error 96 | 97 | if c.config != nil { 98 | err = c.loadClient() 99 | } else { 100 | kubeconfig := os.Getenv("KUBECONFIG") 101 | if len(kubeconfig) > 0 { 102 | err = c.loadClientFromKubeconfig(kubeconfig) 103 | } else { 104 | err = c.loadClientFromServiceAccount() 105 | } 106 | 107 | } 108 | 109 | return err 110 | } 111 | 112 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 113 | func (c *Client) loadClientFromServiceAccount() error { 114 | config, err := rest.InClusterConfig() 115 | if err != nil { 116 | return err 117 | } 118 | 119 | c.config = config 120 | return c.loadClient() 121 | } 122 | 123 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 124 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | c.config = config 130 | return c.loadClient() 131 | } 132 | 133 | func (c *Client) loadClient() error { 134 | if c.config == nil { 135 | return fmt.Errorf("rest config is not provided") 136 | } 137 | 138 | var err error 139 | err = common.SetRateLimiter(c.config) 140 | if err != nil { 141 | return err 142 | } 143 | c.snap, _, err = snapclient.NewClient(c.config) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /k8s/externalstorage/externalstorage_test.go: -------------------------------------------------------------------------------- 1 | package externalstorage 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/kdmp/backuplocationmaintenance.go: -------------------------------------------------------------------------------- 1 | package kdmp 2 | 3 | import ( 4 | "context" 5 | 6 | kdmpv1alpha1 "github.com/portworx/kdmp/pkg/apis/kdmp/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // BackupLocationMaintenanceOps is an interface to perfrom k8s backuplocationmaintenance CR crud operations 11 | type BackupLocationMaintenanceOps interface { 12 | // CreateBackupLocationMaintenance creates the BackupLocationMaintenance CR 13 | CreateBackupLocationMaintenance(*kdmpv1alpha1.BackupLocationMaintenance) (*kdmpv1alpha1.BackupLocationMaintenance, error) 14 | // GetBackupLocationMaintenance gets the BackupLocationMaintenanc CR 15 | GetBackupLocationMaintenance(string, string) (*kdmpv1alpha1.BackupLocationMaintenance, error) 16 | // ListBackupLocationMaintenance lists all the BackupLocationMaintenance CRs 17 | ListBackupLocationMaintenance(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.BackupLocationMaintenanceList, error) 18 | // UpdateBackupLocationMaintenance updates the BackupLocationMaintenance CR 19 | UpdateBackupLocationMaintenance(*kdmpv1alpha1.BackupLocationMaintenance) (*kdmpv1alpha1.BackupLocationMaintenance, error) 20 | // DeleteBackupLocationMaintenance deletes the BackupLocationMaintenance CR 21 | DeleteBackupLocationMaintenance(string, string) error 22 | } 23 | 24 | // CreateBackupLocationMaintenance creates the BackupLocationMaintenance CR 25 | func (c *Client) CreateBackupLocationMaintenance(blMaintenance *kdmpv1alpha1.BackupLocationMaintenance) (*kdmpv1alpha1.BackupLocationMaintenance, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.kdmp.KdmpV1alpha1().BackupLocationMaintenances(blMaintenance.Namespace).Create(context.TODO(), blMaintenance, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetBackupLocationMaintenance gets the BackupLocationMaintenance CR 33 | func (c *Client) GetBackupLocationMaintenance(name, namespace string) (*kdmpv1alpha1.BackupLocationMaintenance, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.kdmp.KdmpV1alpha1().BackupLocationMaintenances(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListBackupLocationMaintenance lists all the BackupLocationMaintenance CR 41 | func (c *Client) ListBackupLocationMaintenance(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.BackupLocationMaintenanceList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.kdmp.KdmpV1alpha1().BackupLocationMaintenances(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // DeleteBackupLocationMaintenance deletes the BackupLocationMaintenance CR 49 | func (c *Client) DeleteBackupLocationMaintenance(name string, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.kdmp.KdmpV1alpha1().BackupLocationMaintenances(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // UpdateBackupLocationMaintenance updates the BackupLocationMaintenance CR 59 | func (c *Client) UpdateBackupLocationMaintenance(blMaintenance *kdmpv1alpha1.BackupLocationMaintenance) (*kdmpv1alpha1.BackupLocationMaintenance, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.kdmp.KdmpV1alpha1().BackupLocationMaintenances(blMaintenance.Namespace).Update(context.TODO(), blMaintenance, metav1.UpdateOptions{}) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/kdmp/resourcebackup.go: -------------------------------------------------------------------------------- 1 | package kdmp 2 | 3 | import ( 4 | "context" 5 | 6 | kdmpv1alpha1 "github.com/portworx/kdmp/pkg/apis/kdmp/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ResourceBackupOps is an interface to perform k8s ResourceExport CR crud operations 11 | type ResourceBackupOps interface { 12 | // CreateResourceExport creates the ResourceExport CR 13 | CreateResourceBackup(*kdmpv1alpha1.ResourceBackup) (*kdmpv1alpha1.ResourceBackup, error) 14 | // GetResourceBackup gets the ResourceBackup CR 15 | GetResourceBackup(string, string) (*kdmpv1alpha1.ResourceBackup, error) 16 | // ListResourceBackup lists all the ResourceBackup CRs 17 | ListResourceBackup(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.ResourceBackupList, error) 18 | // UpdateResourceBackup updates the ResourceBackup CR 19 | UpdateResourceBackup(*kdmpv1alpha1.ResourceBackup) (*kdmpv1alpha1.ResourceBackup, error) 20 | // DeleteResourceBackup deletes the ResourceBackup CR 21 | DeleteResourceBackup(string, string) error 22 | } 23 | 24 | // CreateResourceBackup creates the ResourceBackup CR 25 | func (c *Client) CreateResourceBackup(backup *kdmpv1alpha1.ResourceBackup) (*kdmpv1alpha1.ResourceBackup, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.kdmp.KdmpV1alpha1().ResourceBackups(backup.Namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetResourceBackup gets the ResourceBackup CR 33 | func (c *Client) GetResourceBackup(name, namespace string) (*kdmpv1alpha1.ResourceBackup, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.kdmp.KdmpV1alpha1().ResourceBackups(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListResourceBackup lists all the ResourceBackup CR 41 | func (c *Client) ListResourceBackup(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.ResourceBackupList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.kdmp.KdmpV1alpha1().ResourceBackups(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // DeleteResourceBackup deletes the ResourceBackup CR 49 | func (c *Client) DeleteResourceBackup(name string, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.kdmp.KdmpV1alpha1().ResourceBackups(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // UpdateResourceBackup deletes the ResourceBackup CR 59 | func (c *Client) UpdateResourceBackup(backup *kdmpv1alpha1.ResourceBackup) (*kdmpv1alpha1.ResourceBackup, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.kdmp.KdmpV1alpha1().ResourceBackups(backup.Namespace).Update(context.TODO(), backup, metav1.UpdateOptions{}) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/kdmp/resourceexport.go: -------------------------------------------------------------------------------- 1 | package kdmp 2 | 3 | import ( 4 | "context" 5 | 6 | kdmpv1alpha1 "github.com/portworx/kdmp/pkg/apis/kdmp/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ResourceExportOps is an interface to perform k8s ResourceExport CR crud operations 11 | type ResourceExportOps interface { 12 | // CreateResourceExport creates the ResourceExport CR 13 | CreateResourceExport(*kdmpv1alpha1.ResourceExport) (*kdmpv1alpha1.ResourceExport, error) 14 | // GetResourceExport gets the ResourceExport CR 15 | GetResourceExport(string, string) (*kdmpv1alpha1.ResourceExport, error) 16 | // ListResourceExport lists all the ResourceExport CRs 17 | ListResourceExport(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.ResourceExportList, error) 18 | // UpdateResourceExport updates the ResourceExport CR 19 | UpdateResourceExport(*kdmpv1alpha1.ResourceExport) (*kdmpv1alpha1.ResourceExport, error) 20 | // DeleteResourceExport deletes the ResourceExport CR 21 | DeleteResourceExport(string, string) error 22 | } 23 | 24 | // CreateResourceExport creates the ResourceExport CR 25 | func (c *Client) CreateResourceExport(export *kdmpv1alpha1.ResourceExport) (*kdmpv1alpha1.ResourceExport, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.kdmp.KdmpV1alpha1().ResourceExports(export.Namespace).Create(context.TODO(), export, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetResourceExport gets the ResourceExport CR 33 | func (c *Client) GetResourceExport(name, namespace string) (*kdmpv1alpha1.ResourceExport, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.kdmp.KdmpV1alpha1().ResourceExports(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListResourceExport lists all the ResourceExport CR 41 | func (c *Client) ListResourceExport(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.ResourceExportList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.kdmp.KdmpV1alpha1().ResourceExports(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // DeleteResourceExport deletes the ResourceExport CR 49 | func (c *Client) DeleteResourceExport(name string, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.kdmp.KdmpV1alpha1().ResourceExports(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // UpdateResourceExport deletes the ResourceExport CR 59 | func (c *Client) UpdateResourceExport(export *kdmpv1alpha1.ResourceExport) (*kdmpv1alpha1.ResourceExport, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.kdmp.KdmpV1alpha1().ResourceExports(export.Namespace).Update(context.TODO(), export, metav1.UpdateOptions{}) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/kdmp/volumebackup.go: -------------------------------------------------------------------------------- 1 | package kdmp 2 | 3 | import ( 4 | "context" 5 | 6 | kdmpv1alpha1 "github.com/portworx/kdmp/pkg/apis/kdmp/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // VolumeBackupOps is an interface to perfrom k8s volume backup CR crud operations 11 | type VolumeBackupOps interface { 12 | // CreateVolumeBackup creates the VolumeBackup CR 13 | CreateVolumeBackup(*kdmpv1alpha1.VolumeBackup) (*kdmpv1alpha1.VolumeBackup, error) 14 | // GetVolumeBackup gets the VolumeBackup CR 15 | GetVolumeBackup(string, string) (*kdmpv1alpha1.VolumeBackup, error) 16 | // ListVolumeBackup lists all the VolumeBackup CRs 17 | ListVolumeBackup(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.VolumeBackupList, error) 18 | // UpdateVolumeBackup updates the VolumeBackup CR 19 | UpdateVolumeBackup(*kdmpv1alpha1.VolumeBackup) (*kdmpv1alpha1.VolumeBackup, error) 20 | // DeleteVolumeBackup deletes the VolumeBackup CR 21 | DeleteVolumeBackup(string, string) error 22 | } 23 | 24 | // CreateVolumeBackup creates the VolumeBackup CR 25 | func (c *Client) CreateVolumeBackup(backup *kdmpv1alpha1.VolumeBackup) (*kdmpv1alpha1.VolumeBackup, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.kdmp.KdmpV1alpha1().VolumeBackups(backup.Namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetVolumeBackup gets the VolumeBackup CR 33 | func (c *Client) GetVolumeBackup(name, namespace string) (*kdmpv1alpha1.VolumeBackup, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.kdmp.KdmpV1alpha1().VolumeBackups(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListVolumeBackup lists all the VolumeBackup CR 41 | func (c *Client) ListVolumeBackup(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.VolumeBackupList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.kdmp.KdmpV1alpha1().VolumeBackups(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // DeleteVolumeBackup deletes the VolumeBackup CR 49 | func (c *Client) DeleteVolumeBackup(name string, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.kdmp.KdmpV1alpha1().VolumeBackups(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // UpdateVolumeBackup updates the VolumeBackup CR 59 | func (c *Client) UpdateVolumeBackup(backup *kdmpv1alpha1.VolumeBackup) (*kdmpv1alpha1.VolumeBackup, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.kdmp.KdmpV1alpha1().VolumeBackups(backup.Namespace).Update(context.TODO(), backup, metav1.UpdateOptions{}) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/kdmp/volumebackupdelete.go: -------------------------------------------------------------------------------- 1 | package kdmp 2 | 3 | import ( 4 | "context" 5 | 6 | kdmpv1alpha1 "github.com/portworx/kdmp/pkg/apis/kdmp/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // VolumeBackupDeleteOps is an interface to perfrom k8s volume delete CR crud operations 11 | type VolumeBackupDeleteOps interface { 12 | // CreateVolumeBackupDelete creates the VolumeBackupDelete CR 13 | CreateVolumeBackupDelete(*kdmpv1alpha1.VolumeBackupDelete) (*kdmpv1alpha1.VolumeBackupDelete, error) 14 | // GetVolumeBackupDelete gets the VolumeBackupDelete CR 15 | GetVolumeBackupDelete(string, string) (*kdmpv1alpha1.VolumeBackupDelete, error) 16 | // ListVolumeBackupDelete lists all the VolumeBackupDelete CRs 17 | ListVolumeBackupDelete(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.VolumeBackupDeleteList, error) 18 | // UpdateVolumeBackupDelete updates the VolumeBackupDelete CR 19 | UpdateVolumeBackupDelete(*kdmpv1alpha1.VolumeBackupDelete) (*kdmpv1alpha1.VolumeBackupDelete, error) 20 | // DeleteVolumeBackupDelete deletes the VolumeBackupDelete CR 21 | DeleteVolumeBackupDelete(string, string) error 22 | } 23 | 24 | // CreateVolumeBackupDelete creates the VolumeBackupDelete CR 25 | func (c *Client) CreateVolumeBackupDelete(delete *kdmpv1alpha1.VolumeBackupDelete) (*kdmpv1alpha1.VolumeBackupDelete, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.kdmp.KdmpV1alpha1().VolumeBackupDeletes(delete.Namespace).Create(context.TODO(), delete, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetVolumeBackupDelete gets the VolumeBackupDelete CR 33 | func (c *Client) GetVolumeBackupDelete(name, namespace string) (*kdmpv1alpha1.VolumeBackupDelete, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.kdmp.KdmpV1alpha1().VolumeBackupDeletes(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListVolumeBackupDelete lists all the VolumeBackupDelete CR 41 | func (c *Client) ListVolumeBackupDelete(namespace string, filterOptions metav1.ListOptions) (*kdmpv1alpha1.VolumeBackupDeleteList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.kdmp.KdmpV1alpha1().VolumeBackupDeletes(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // DeleteVolumeBackupDelete deletes the VolumeBackupDelete CR 49 | func (c *Client) DeleteVolumeBackupDelete(name string, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.kdmp.KdmpV1alpha1().VolumeBackupDeletes(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // UpdateVolumeBackupDelete updates the VolumeBackupDelete CR 59 | func (c *Client) UpdateVolumeBackupDelete(delete *kdmpv1alpha1.VolumeBackupDelete) (*kdmpv1alpha1.VolumeBackupDelete, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.kdmp.KdmpV1alpha1().VolumeBackupDeletes(delete.Namespace).Update(context.TODO(), delete, metav1.UpdateOptions{}) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/datavolume_test.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | func TestGetDataVolume(t *testing.T) { 13 | // Populate the variables below from a live cluster to test this manually. 14 | testKubeconfig := "TBD" 15 | testDVNamespace := "TBD" 16 | testDVName := "TBD" 17 | 18 | if testKubeconfig == "TBD" { 19 | t.Skip("Populate the test variables to run this test manually.") 20 | } 21 | os.Setenv("KUBECONFIG", testKubeconfig) 22 | 23 | Instance() 24 | 25 | require.NotNil(t, instance, "instance should be initialized") 26 | vmi, err := instance.GetDataVolume(context.TODO(), testDVNamespace, testDVName) 27 | if err != nil { 28 | t.Logf("Failed to get data volume: %v", err) 29 | t.FailNow() 30 | } 31 | t.Logf("Data volume: %v", vmi) 32 | } 33 | 34 | func TestListDataVolumes(t *testing.T) { 35 | // Populate the variables below from a live cluster to test this manually. 36 | testKubeconfig := "TBD" 37 | testDVNamespace := "TBD" 38 | 39 | if testKubeconfig == "TBD" { 40 | t.Skip("Populate the test variables to run this test manually.") 41 | } 42 | os.Setenv("KUBECONFIG", testKubeconfig) 43 | 44 | Instance() 45 | 46 | require.NotNil(t, instance, "instance should be initialized") 47 | dvs, err := instance.ListDataVolumes( 48 | context.TODO(), testDVNamespace, metav1.ListOptions{}) 49 | if err != nil { 50 | t.Logf("Failed to list data volume: %v", err) 51 | t.FailNow() 52 | } 53 | for i, dv := range dvs { 54 | t.Logf("Data volume %d:", i) 55 | t.Logf("================") 56 | t.Logf("%v\n\n", dv) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/kubevirt_test.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/virtualmachine.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 9 | "k8s.io/apimachinery/pkg/runtime/schema" 10 | ) 11 | 12 | var ( 13 | vmResource = schema.GroupVersionResource{Group: "kubevirt.io", Version: "v1", Resource: "virtualmachines"} 14 | ) 15 | 16 | // VirtualMachine represents an instance of KubeVirt VirtualMachine 17 | type VirtualMachine struct { 18 | // Name if the VM 19 | Name string 20 | // Namespace of the VM 21 | NameSpace string 22 | // UID of the VM 23 | UID string 24 | // Created indicates if the virtual machine has been created in the cluster 25 | Created bool 26 | // Ready indicates if the virtual machine is running and ready 27 | Ready bool 28 | } 29 | 30 | // VirtualMachineOps is an interface to manage VirtualMachineInstance objects 31 | type VirtualMachineOps interface { 32 | // GetVirtualMachine retrieves some info about the specified VM 33 | GetVirtualMachine(ctx context.Context, namespace, name string) (*VirtualMachine, error) 34 | // ListVirtualMachines retrieves VMs in the specified namespace 35 | ListVirtualMachines(ctx context.Context, namespace string, opts metav1.ListOptions) ([]*VirtualMachine, error) 36 | } 37 | 38 | // GetVirtualMachine returns the VirtualMachine 39 | func (c *Client) GetVirtualMachine(ctx context.Context, namespace, name string) (*VirtualMachine, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | vmRaw, err := c.client.Resource(vmResource).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return c.unstructuredGetVM(vmRaw) 48 | } 49 | 50 | // ListVirtualMachines returns the VirtualMachines 51 | func (c *Client) ListVirtualMachines( 52 | ctx context.Context, namespace string, opts metav1.ListOptions, 53 | ) ([]*VirtualMachine, error) { 54 | var ret []*VirtualMachine 55 | if err := c.initClient(); err != nil { 56 | return nil, err 57 | } 58 | rawVMs, err := c.client.Resource(vmResource).Namespace(namespace).List(ctx, opts) 59 | if err != nil { 60 | return nil, err 61 | } 62 | for _, rawVM := range rawVMs.Items { 63 | vm, err := c.unstructuredGetVM(&rawVM) 64 | if err != nil { 65 | return nil, fmt.Errorf("failed to parse unstructured VM object: %w", err) 66 | } 67 | ret = append(ret, vm) 68 | } 69 | return ret, nil 70 | } 71 | 72 | // unstructuredGetVM 73 | func (c *Client) unstructuredGetVM(vmRaw *unstructured.Unstructured) (*VirtualMachine, error) { 74 | // metadata: 75 | // name: test-vm-csi 76 | // namespace: kubevirt-fedora-vm 77 | // uid: ed990548-5f16-4d6e-8b26-6e0acbc1a944 78 | name, found, err := unstructured.NestedString(vmRaw.Object, "metadata", "name") 79 | if err != nil || !found { 80 | return nil, fmt.Errorf("failed to find vm name: %w", err) 81 | } 82 | namespace, found, err := unstructured.NestedString(vmRaw.Object, "metadata", "namespace") 83 | if err != nil || !found { 84 | return nil, fmt.Errorf("failed to find vm namespace: %w", err) 85 | } 86 | uid, found, err := unstructured.NestedString(vmRaw.Object, "metadata", "uid") 87 | if err != nil || !found { 88 | return nil, fmt.Errorf("failed to find vm uid: %w", err) 89 | } 90 | 91 | // created 92 | created, _, err := unstructured.NestedBool(vmRaw.Object, "status", "created") 93 | if err != nil { 94 | return nil, fmt.Errorf("failed to find vm created status: %w", err) 95 | } 96 | 97 | // ready 98 | ready, _, err := unstructured.NestedBool(vmRaw.Object, "status", "ready") 99 | if err != nil { 100 | return nil, fmt.Errorf("failed to find vm ready status: %w", err) 101 | } 102 | 103 | return &VirtualMachine{ 104 | Name: name, 105 | NameSpace: namespace, 106 | UID: uid, 107 | Created: created, 108 | Ready: ready, 109 | }, nil 110 | } 111 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/virtualmachine_test.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | func TestGetVM(t *testing.T) { 13 | // Populate the variables below from a live cluster to test this manually. 14 | testKubeconfig := "TBD" 15 | testVMNamespace := "TBD" 16 | testVMName := "TBD" 17 | 18 | if testKubeconfig == "TBD" { 19 | t.Skip("Populate the test variables to run this test manually.") 20 | } 21 | os.Setenv("KUBECONFIG", testKubeconfig) 22 | 23 | Instance() 24 | 25 | require.NotNil(t, instance, "instance should be initialized") 26 | vm, err := instance.GetVirtualMachine(context.TODO(), testVMNamespace, testVMName) 27 | if err != nil { 28 | t.Logf("Failed to get VM: %v", err) 29 | t.FailNow() 30 | } 31 | t.Logf("VM: %v", vm) 32 | } 33 | 34 | func TestListVMs(t *testing.T) { 35 | // Populate the variables below from a live cluster to test this manually. 36 | testKubeconfig := "TBD" 37 | testVMNamespace := "TBD" 38 | 39 | if testKubeconfig == "TBD" { 40 | t.Skip("Populate the test variables to run this test manually.") 41 | } 42 | os.Setenv("KUBECONFIG", testKubeconfig) 43 | 44 | Instance() 45 | 46 | require.NotNil(t, instance, "instance should be initialized") 47 | vms, err := instance.ListVirtualMachines(context.TODO(), testVMNamespace, metav1.ListOptions{}) 48 | if err != nil { 49 | t.Logf("Failed to get VMs: %v", err) 50 | t.FailNow() 51 | } 52 | for i, vm := range vms { 53 | t.Logf("VM #%d: %v", i, vm) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/virtualmachineinstance_test.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestGetVMI(t *testing.T) { 12 | // Populate the variables below from a live cluster to test this manually. 13 | testKubeconfig := "TBD" 14 | testVMINamespace := "TBD" 15 | testVMIName := "TBD" 16 | 17 | if testKubeconfig == "TBD" { 18 | t.Skip("Populate the test variables to run this test manually.") 19 | } 20 | os.Setenv("KUBECONFIG", testKubeconfig) 21 | 22 | Instance() 23 | 24 | require.NotNil(t, instance, "instance should be initialized") 25 | vmi, err := instance.GetVirtualMachineInstance(context.TODO(), testVMINamespace, testVMIName) 26 | if err != nil { 27 | t.Logf("Failed to get VMI: %v", err) 28 | t.FailNow() 29 | } 30 | t.Logf("VMI: %v", vmi) 31 | t.Logf("LiveMigratable=%v, Ready=%v, Paused=%v", vmi.LiveMigratable, vmi.Ready, vmi.Paused) 32 | for _, phaseTransition := range vmi.PhaseTransitions { 33 | t.Logf("PhaseTransition: %v", phaseTransition) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /k8s/kubevirt-dynamic/virtualmachineinstancemigration_test.go: -------------------------------------------------------------------------------- 1 | package kubevirtdynamic 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | func TestGetMigration(t *testing.T) { 13 | // Populate the variables below from a live cluster to test this manually. 14 | testKubeconfig := "TBD" 15 | testMigrationNamespace := "TBD" 16 | testMigrationName := "TBD" 17 | 18 | if testKubeconfig == "TBD" { 19 | t.Skip("Populate the test variables to run this test manually.") 20 | } 21 | os.Setenv("KUBECONFIG", testKubeconfig) 22 | 23 | Instance() 24 | 25 | require.NotNil(t, instance, "instance should be initialized") 26 | migration, err := instance.GetVirtualMachineInstanceMigration( 27 | context.TODO(), testMigrationNamespace, testMigrationName) 28 | if err != nil { 29 | t.Logf("Failed to get migration: %v", err) 30 | } 31 | t.Logf("Migration: %v", migration) 32 | } 33 | 34 | func TestCreateMigration(t *testing.T) { 35 | // Populate the variables below from a live cluster to test this manually. 36 | testKubeconfig := "TBD" 37 | testVMINamespace := "TBD" 38 | testVMIName := "TBD" 39 | 40 | if testKubeconfig == "TBD" { 41 | t.Skip("Populate the test variables to run this test manually.") 42 | } 43 | os.Setenv("KUBECONFIG", testKubeconfig) 44 | 45 | Instance() 46 | 47 | require.NotNil(t, instance, "instance should be initialized") 48 | migration, err := instance.CreateVirtualMachineInstanceMigration(context.TODO(), testVMINamespace, testVMIName) 49 | if err != nil { 50 | t.Logf("Failed to create migration: %v", err) 51 | } 52 | t.Logf("Migration: %v", migration) 53 | } 54 | 55 | func TestCreateMigrationWithParams(t *testing.T) { 56 | // Populate the variables below from a live cluster to test this manually. 57 | testKubeconfig := "TBD" 58 | testVMINamespace := "TBD" 59 | testVMIName := "TBD" 60 | 61 | if testKubeconfig == "TBD" { 62 | t.Skip("Populate the test variables to run this test manually.") 63 | } 64 | os.Setenv("KUBECONFIG", testKubeconfig) 65 | 66 | Instance() 67 | 68 | require.NotNil(t, instance, "instance should be initialized") 69 | 70 | migration, err := instance.CreateVirtualMachineInstanceMigrationWithParams( 71 | context.TODO(), testVMINamespace, testVMIName, "migration1", "", 72 | map[string]string{"anno1": "val1"}, map[string]string{"label2": "val2"}) 73 | if err != nil { 74 | t.Logf("Failed to create migration: %v", err) 75 | } 76 | t.Logf("Migration: %v", migration) 77 | } 78 | 79 | func TestListMigrations(t *testing.T) { 80 | // Populate the variables below from a live cluster to test this manually. 81 | testKubeconfig := "TBD" 82 | testMigrationNamespace := "TBD" 83 | 84 | if testKubeconfig == "TBD" { 85 | t.Skip("Populate the test variables to run this test manually.") 86 | } 87 | os.Setenv("KUBECONFIG", testKubeconfig) 88 | 89 | Instance() 90 | 91 | require.NotNil(t, instance, "instance should be initialized") 92 | migrations, err := instance.ListVirtualMachineInstanceMigrations( 93 | context.TODO(), testMigrationNamespace, metav1.ListOptions{}) 94 | if err != nil { 95 | t.Logf("Failed to list migrations: %v", err) 96 | t.FailNow() 97 | } 98 | for i, migration := range migrations { 99 | t.Logf("Migration %d:", i) 100 | t.Logf("================") 101 | t.Logf("%v\n\n", migration) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /k8s/kubevirt/virtualmachineinstance.go: -------------------------------------------------------------------------------- 1 | package kubevirt 2 | 3 | import ( 4 | "context" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | kubevirtv1 "kubevirt.io/api/core/v1" 7 | ) 8 | 9 | // VirtualMachineInstanceOps is an interface to perform kubevirt operations on virtual machine instances 10 | type VirtualMachineInstanceOps interface { 11 | // GetVirtualMachineInstance gets updated Virtual Machine Instance from client matching name and namespace 12 | GetVirtualMachineInstance(context.Context, string, string) (*kubevirtv1.VirtualMachineInstance, error) 13 | } 14 | 15 | // GetVirtualMachineInstance gets updated Virtual Machine Instance from client matching name and namespace 16 | func (c *Client) GetVirtualMachineInstance(ctx context.Context, name string, namespace string) (*kubevirtv1.VirtualMachineInstance, error) { 17 | if err := c.initClient(); err != nil { 18 | return nil, err 19 | } 20 | 21 | return c.kubevirt.VirtualMachineInstance(namespace).Get(ctx, name, &metav1.GetOptions{}) 22 | } 23 | -------------------------------------------------------------------------------- /k8s/networking/ingress.go: -------------------------------------------------------------------------------- 1 | package networking 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | schederrors "github.com/portworx/sched-ops/k8s/errors" 9 | "github.com/portworx/sched-ops/task" 10 | v1beta1 "k8s.io/api/networking/v1beta1" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | ) 13 | 14 | // IngressOps is an interface to perform kubernetes related operations on the crd resources. 15 | type IngressOps interface { 16 | // CreateIngress creates the given ingress 17 | CreateIngress(ingress *v1beta1.Ingress) (*v1beta1.Ingress, error) 18 | // UpdateIngress creates the given ingress 19 | UpdateIngress(ingress *v1beta1.Ingress) (*v1beta1.Ingress, error) 20 | // GetIngress returns the ingress given name and namespace 21 | GetIngress(name, namespace string) (*v1beta1.Ingress, error) 22 | // DeleteIngress deletes the given ingress 23 | DeleteIngress(name, namespace string) error 24 | // ValidateIngress validates the given ingress 25 | ValidateIngress(ingress *v1beta1.Ingress, timeout, retryInterval time.Duration) error 26 | } 27 | 28 | // NamespaceDefault default namespace for ingress 29 | var NamespaceDefault = "default" 30 | 31 | // CreateIngress creates the given ingress 32 | func (c *Client) CreateIngress(ingress *v1beta1.Ingress) (*v1beta1.Ingress, error) { 33 | if err := c.initClient(); err != nil { 34 | return nil, err 35 | } 36 | 37 | ns := ingress.Namespace 38 | if len(ns) == 0 { 39 | ns = NamespaceDefault 40 | } 41 | 42 | return c.networking.Ingresses(ns).Create(context.TODO(), ingress, metav1.CreateOptions{}) 43 | } 44 | 45 | // UpdateIngress creates the given ingress 46 | func (c *Client) UpdateIngress(ingress *v1beta1.Ingress) (*v1beta1.Ingress, error) { 47 | if err := c.initClient(); err != nil { 48 | return nil, err 49 | } 50 | 51 | return c.networking.Ingresses(ingress.Namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{}) 52 | } 53 | 54 | // GetIngress returns the ingress given name and namespace 55 | func (c *Client) GetIngress(name, namespace string) (*v1beta1.Ingress, error) { 56 | if err := c.initClient(); err != nil { 57 | return nil, err 58 | } 59 | 60 | return c.networking.Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 61 | } 62 | 63 | // DeleteIngress deletes the given ingress 64 | func (c *Client) DeleteIngress(name, namespace string) error { 65 | if err := c.initClient(); err != nil { 66 | return err 67 | } 68 | 69 | return c.networking.Ingresses(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 70 | } 71 | 72 | // ValidateIngress validates the given ingress 73 | func (c *Client) ValidateIngress(ingress *v1beta1.Ingress, timeout, retryInterval time.Duration) error { 74 | t := func() (interface{}, bool, error) { 75 | if err := c.initClient(); err != nil { 76 | return "", true, err 77 | } 78 | 79 | result, err := c.networking.Ingresses(ingress.Namespace).Get(context.TODO(), ingress.Name, metav1.GetOptions{}) 80 | if result == nil { 81 | return "", true, err 82 | } 83 | if len(result.Status.LoadBalancer.Ingress) < 1 { 84 | return "", true, &schederrors.ErrAppNotReady{ 85 | ID: ingress.Name, 86 | Cause: fmt.Sprintf("Failed to set load balancer for ingress. %sErr: %v", ingress.Name, err), 87 | } 88 | } 89 | return "", false, nil 90 | } 91 | if _, err := task.DoRetryWithTimeout(t, timeout, retryInterval); err != nil { 92 | return err 93 | } 94 | return nil 95 | 96 | } 97 | -------------------------------------------------------------------------------- /k8s/networking/networking.go: -------------------------------------------------------------------------------- 1 | package networking 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/portworx/sched-ops/k8s/common" 9 | networkingv1betaclient "k8s.io/client-go/kubernetes/typed/networking/v1beta1" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops is an interface to perform kubernetes related operations on the networking resources. 20 | type Ops interface { 21 | IngressOps 22 | 23 | // SetConfig sets the config and resets the client 24 | SetConfig(config *rest.Config) 25 | } 26 | 27 | // Instance returns a singleton instance of the client. 28 | func Instance() Ops { 29 | once.Do(func() { 30 | if instance == nil { 31 | instance = &Client{} 32 | } 33 | }) 34 | return instance 35 | } 36 | 37 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 38 | func SetInstance(i Ops) { 39 | instance = i 40 | } 41 | 42 | // New builds a new networking client. 43 | func New(networking networkingv1betaclient.NetworkingV1beta1Interface) *Client { 44 | return &Client{ 45 | networking: networking, 46 | } 47 | } 48 | 49 | // NewForConfig builds a new networking client for the given config. 50 | func NewForConfig(c *rest.Config) (*Client, error) { 51 | networking, err := networkingv1betaclient.NewForConfig(c) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return &Client{ 57 | networking: networking, 58 | }, nil 59 | } 60 | 61 | // NewInstanceFromConfigFile returns new instance of client by using given 62 | // config file 63 | func NewInstanceFromConfigFile(config string) (Ops, error) { 64 | newInstance := &Client{} 65 | err := newInstance.loadClientFromKubeconfig(config) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return newInstance, nil 70 | } 71 | 72 | // Client provides a wrapper for the kubernetes networking client. 73 | type Client struct { 74 | config *rest.Config 75 | networking networkingv1betaclient.NetworkingV1beta1Interface 76 | } 77 | 78 | // SetConfig sets the config and resets the client 79 | func (c *Client) SetConfig(cfg *rest.Config) { 80 | c.config = cfg 81 | c.networking = nil 82 | } 83 | 84 | // initClient the k8s client if uninitialized 85 | func (c *Client) initClient() error { 86 | if c.networking != nil { 87 | return nil 88 | } 89 | 90 | return c.setClient() 91 | } 92 | 93 | // setClient instantiates a client. 94 | func (c *Client) setClient() error { 95 | var err error 96 | 97 | if c.config != nil { 98 | err = c.loadClient() 99 | } else { 100 | kubeconfig := os.Getenv("KUBECONFIG") 101 | if len(kubeconfig) > 0 { 102 | err = c.loadClientFromKubeconfig(kubeconfig) 103 | } else { 104 | err = c.loadClientFromServiceAccount() 105 | } 106 | 107 | } 108 | 109 | return err 110 | } 111 | 112 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 113 | func (c *Client) loadClientFromServiceAccount() error { 114 | config, err := rest.InClusterConfig() 115 | if err != nil { 116 | return err 117 | } 118 | 119 | c.config = config 120 | return c.loadClient() 121 | } 122 | 123 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 124 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | c.config = config 130 | return c.loadClient() 131 | } 132 | 133 | func (c *Client) loadClient() error { 134 | if c.config == nil { 135 | return fmt.Errorf("rest config is not provided") 136 | } 137 | 138 | var err error 139 | err = common.SetRateLimiter(c.config) 140 | if err != nil { 141 | return err 142 | } 143 | c.networking, err = networkingv1betaclient.NewForConfig(c.config) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /k8s/networking/networking_test.go: -------------------------------------------------------------------------------- 1 | package networking 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/openshift/config.go: -------------------------------------------------------------------------------- 1 | package openshift 2 | 3 | import ( 4 | "context" 5 | 6 | openshiftv1 "github.com/openshift/api/config/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ConfigOps interface for openshift config operations 11 | type ConfigOps interface { 12 | // GetClusterVersion get cluster version for the given name 13 | GetClusterVersion(string) (*openshiftv1.ClusterVersion, error) 14 | } 15 | 16 | // GetClusterVersion get cluster version for the given name 17 | func (c *Client) GetClusterVersion(name string) (*openshiftv1.ClusterVersion, error) { 18 | if err := c.initClient(); err != nil { 19 | return nil, err 20 | } 21 | return c.ocpConfigClient.ConfigV1().ClusterVersions().Get(context.TODO(), name, metav1.GetOptions{}) 22 | } 23 | -------------------------------------------------------------------------------- /k8s/openshift/openshift_test.go: -------------------------------------------------------------------------------- 1 | package openshift 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/openshift/securitycontextconstraints.go: -------------------------------------------------------------------------------- 1 | package openshift 2 | 3 | import ( 4 | "context" 5 | 6 | ocpsecurityv1api "github.com/openshift/api/security/v1" 7 | ocpsecurityv1client "github.com/openshift/client-go/security/clientset/versioned/typed/security/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // SecurityContextConstraintsOps is an interface to list, get and update security context constraints 12 | type SecurityContextConstraintsOps interface { 13 | // ListSecurityContextConstraints returns the list of all SecurityContextConstraints, and an error if there is any. 14 | ListSecurityContextConstraints() (*ocpsecurityv1api.SecurityContextConstraintsList, error) 15 | // GetSecurityContextConstraints takes name of the securityContextConstraints and returns the corresponding securityContextConstraints object, and an error if there is any. 16 | GetSecurityContextConstraints(string) (*ocpsecurityv1api.SecurityContextConstraints, error) 17 | // CreateSecurityContextConstraints creates securityContextConstraints 18 | CreateSecurityContextConstraints(*ocpsecurityv1api.SecurityContextConstraints) (*ocpsecurityv1api.SecurityContextConstraints, error) 19 | // UpdateSecurityContextConstraints takes the representation of a securityContextConstraints and updates it. Returns the server's representation of the securityContextConstraints, and an error, if there is any. 20 | UpdateSecurityContextConstraints(*ocpsecurityv1api.SecurityContextConstraints) (*ocpsecurityv1api.SecurityContextConstraints, error) 21 | } 22 | 23 | func (c *Client) getOcpSecurityClient() ocpsecurityv1client.SecurityV1Interface { 24 | return c.ocpSecurityClient.SecurityV1() 25 | } 26 | 27 | // ListSecurityContextConstraints returns the list of all SecurityContextConstraints, and an error if there is any. 28 | func (c *Client) ListSecurityContextConstraints() (result *ocpsecurityv1api.SecurityContextConstraintsList, err error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | return c.getOcpSecurityClient().SecurityContextConstraints().List(context.TODO(), metav1.ListOptions{}) 33 | } 34 | 35 | // GetSecurityContextConstraints takes name of the securityContextConstraints and returns the corresponding securityContextConstraints object, and an error if there is any. 36 | func (c *Client) GetSecurityContextConstraints(name string) (result *ocpsecurityv1api.SecurityContextConstraints, err error) { 37 | if err := c.initClient(); err != nil { 38 | return nil, err 39 | } 40 | return c.getOcpSecurityClient().SecurityContextConstraints().Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // CreateSecurityContextConstraints creates securityContextConstraints 44 | func (c *Client) CreateSecurityContextConstraints(securityContextConstraints *ocpsecurityv1api.SecurityContextConstraints) (result *ocpsecurityv1api.SecurityContextConstraints, err error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | return c.getOcpSecurityClient().SecurityContextConstraints().Create(context.TODO(), securityContextConstraints, metav1.CreateOptions{}) 49 | } 50 | 51 | // UpdateSecurityContextConstraints takes the representation of a securityContextConstraints and updates it. Returns the server's representation of the securityContextConstraints, and an error, if there is any. 52 | func (c *Client) UpdateSecurityContextConstraints(securityContextConstraints *ocpsecurityv1api.SecurityContextConstraints) (result *ocpsecurityv1api.SecurityContextConstraints, err error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | return c.getOcpSecurityClient().SecurityContextConstraints().Update(context.TODO(), securityContextConstraints, metav1.UpdateOptions{}) 57 | } 58 | -------------------------------------------------------------------------------- /k8s/operator/operator.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | ostclientset "github.com/libopenstorage/operator/pkg/client/clientset/versioned" 9 | "github.com/portworx/sched-ops/k8s/common" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/client-go/rest" 12 | "k8s.io/client-go/tools/clientcmd" 13 | ) 14 | 15 | var ( 16 | instance Ops 17 | once sync.Once 18 | 19 | deleteForegroundPolicy = metav1.DeletePropagationForeground 20 | ) 21 | 22 | // Ops is an interface to Operator operations. 23 | type Ops interface { 24 | StorageClusterOps 25 | StorageNodeOps 26 | PortworxDiagOps 27 | 28 | // SetConfig sets the config and resets the client 29 | SetConfig(config *rest.Config) 30 | } 31 | 32 | // Instance returns a singleton instance of the client. 33 | func Instance() Ops { 34 | once.Do(func() { 35 | if instance == nil { 36 | instance = &Client{} 37 | } 38 | }) 39 | return instance 40 | } 41 | 42 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 43 | func SetInstance(i Ops) { 44 | instance = i 45 | } 46 | 47 | // New builds a new operator client. 48 | func New(c ostclientset.Interface) *Client { 49 | return &Client{ 50 | ost: c, 51 | } 52 | } 53 | 54 | // NewForConfig builds a new operator client for the given config. 55 | func NewForConfig(c *rest.Config) (*Client, error) { 56 | ostClient, err := ostclientset.NewForConfig(c) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | return &Client{ 62 | ost: ostClient, 63 | }, nil 64 | } 65 | 66 | // NewInstanceFromConfigFile returns new instance of client by using given 67 | // config file 68 | func NewInstanceFromConfigFile(config string) (Ops, error) { 69 | newInstance := &Client{} 70 | err := newInstance.loadClientFromKubeconfig(config) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return newInstance, nil 75 | } 76 | 77 | // Client is a wrapper for the operator client. 78 | type Client struct { 79 | config *rest.Config 80 | ost ostclientset.Interface 81 | } 82 | 83 | // SetConfig sets the config and resets the client 84 | func (c *Client) SetConfig(cfg *rest.Config) { 85 | c.config = cfg 86 | c.ost = nil 87 | } 88 | 89 | // initClient the k8s client if uninitialized 90 | func (c *Client) initClient() error { 91 | if c.ost != nil { 92 | return nil 93 | } 94 | 95 | return c.setClient() 96 | } 97 | 98 | // setClient instantiates a client. 99 | func (c *Client) setClient() error { 100 | var err error 101 | 102 | if c.config != nil { 103 | err = c.loadClient() 104 | } else { 105 | kubeconfig := os.Getenv("KUBECONFIG") 106 | if len(kubeconfig) > 0 { 107 | err = c.loadClientFromKubeconfig(kubeconfig) 108 | } else { 109 | err = c.loadClientFromServiceAccount() 110 | } 111 | 112 | } 113 | 114 | return err 115 | } 116 | 117 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 118 | func (c *Client) loadClientFromServiceAccount() error { 119 | config, err := rest.InClusterConfig() 120 | if err != nil { 121 | return err 122 | } 123 | 124 | c.config = config 125 | return c.loadClient() 126 | } 127 | 128 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 129 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | c.config = config 135 | return c.loadClient() 136 | } 137 | 138 | func (c *Client) loadClient() error { 139 | if c.config == nil { 140 | return fmt.Errorf("rest config is not provided") 141 | } 142 | 143 | var err error 144 | err = common.SetRateLimiter(c.config) 145 | if err != nil { 146 | return err 147 | } 148 | c.ost, err = ostclientset.NewForConfig(c.config) 149 | if err != nil { 150 | return err 151 | } 152 | 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /k8s/operator/operator_test.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/operator/portworxdiag.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | portworxv1 "github.com/libopenstorage/operator/pkg/apis/portworx/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // PortworxDiagOps is an interface to perfrom k8s PortworxDiag operations 11 | type PortworxDiagOps interface { 12 | // CreatePortworxDiag creates the given PortworxDiag 13 | CreatePortworxDiag(*portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) 14 | // UpdatePortworxDiag updates the given PortworxDiag 15 | UpdatePortworxDiag(*portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) 16 | // GetPortworxDiag gets the PortworxDiag with given name and namespace 17 | GetPortworxDiag(string, string) (*portworxv1.PortworxDiag, error) 18 | // ListPortworxDiags lists all the PortworxDiags 19 | ListPortworxDiags(string) (*portworxv1.PortworxDiagList, error) 20 | // DeletePortworxDiag deletes the given PortworxDiag 21 | DeletePortworxDiag(string, string) error 22 | // UpdatePortworxDiagStatus update the status of given PortworxDiag 23 | UpdatePortworxDiagStatus(*portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) 24 | } 25 | 26 | // CreatePortworxDiag creates the given PortworxDiag 27 | func (c *Client) CreatePortworxDiag(diag *portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) { 28 | if err := c.initClient(); err != nil { 29 | return nil, err 30 | } 31 | 32 | ns := diag.Namespace 33 | if len(ns) == 0 { 34 | ns = metav1.NamespaceDefault 35 | } 36 | 37 | return c.ost.PortworxV1().PortworxDiags(ns).Create(context.TODO(), diag, metav1.CreateOptions{}) 38 | } 39 | 40 | // UpdatePortworxDiag updates the given PortworxDiag 41 | func (c *Client) UpdatePortworxDiag(diag *portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | 46 | return c.ost.PortworxV1().PortworxDiags(diag.Namespace).Update(context.TODO(), diag, metav1.UpdateOptions{}) 47 | } 48 | 49 | // GetPortworxDiag gets the PortworxDiag with given name and namespace 50 | func (c *Client) GetPortworxDiag(name, namespace string) (*portworxv1.PortworxDiag, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | return c.ost.PortworxV1().PortworxDiags(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 55 | } 56 | 57 | // ListPortworxDiags lists all the PortworxDiags 58 | func (c *Client) ListPortworxDiags(namespace string) (*portworxv1.PortworxDiagList, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | return c.ost.PortworxV1().PortworxDiags(namespace).List(context.TODO(), metav1.ListOptions{}) 63 | } 64 | 65 | // DeletePortworxDiag deletes the given PortworxDiag 66 | func (c *Client) DeletePortworxDiag(name, namespace string) error { 67 | if err := c.initClient(); err != nil { 68 | return err 69 | } 70 | 71 | return c.ost.PortworxV1().PortworxDiags(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 72 | } 73 | 74 | // UpdatePortworxDiagStatus update the status of given PortworxDiag 75 | func (c *Client) UpdatePortworxDiagStatus(diag *portworxv1.PortworxDiag) (*portworxv1.PortworxDiag, error) { 76 | if err := c.initClient(); err != nil { 77 | return nil, err 78 | } 79 | return c.ost.PortworxV1().PortworxDiags(diag.Namespace).UpdateStatus(context.TODO(), diag, metav1.UpdateOptions{}) 80 | } 81 | -------------------------------------------------------------------------------- /k8s/operator/storagecluster.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "github.com/libopenstorage/operator/pkg/apis/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // StorageClusterOps is an interface to perfrom k8s StorageCluster operations 11 | type StorageClusterOps interface { 12 | // CreateStorageCluster creates the given StorageCluster 13 | CreateStorageCluster(*corev1.StorageCluster) (*corev1.StorageCluster, error) 14 | // UpdateStorageCluster updates the given StorageCluster 15 | UpdateStorageCluster(*corev1.StorageCluster) (*corev1.StorageCluster, error) 16 | // GetStorageCluster gets the StorageCluster with given name and namespace 17 | GetStorageCluster(string, string) (*corev1.StorageCluster, error) 18 | // ListStorageClusters lists all the StorageClusters 19 | ListStorageClusters(string) (*corev1.StorageClusterList, error) 20 | // DeleteStorageCluster deletes the given StorageCluster 21 | DeleteStorageCluster(string, string) error 22 | // UpdateStorageClusterStatus update the status of given StorageCluster 23 | UpdateStorageClusterStatus(*corev1.StorageCluster) (*corev1.StorageCluster, error) 24 | } 25 | 26 | // CreateStorageCluster creates the given StorageCluster 27 | func (c *Client) CreateStorageCluster(cluster *corev1.StorageCluster) (*corev1.StorageCluster, error) { 28 | if err := c.initClient(); err != nil { 29 | return nil, err 30 | } 31 | 32 | ns := cluster.Namespace 33 | if len(ns) == 0 { 34 | ns = metav1.NamespaceDefault 35 | } 36 | 37 | return c.ost.CoreV1().StorageClusters(ns).Create(context.TODO(), cluster, metav1.CreateOptions{}) 38 | } 39 | 40 | // UpdateStorageCluster updates the given StorageCluster 41 | func (c *Client) UpdateStorageCluster(cluster *corev1.StorageCluster) (*corev1.StorageCluster, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | 46 | return c.ost.CoreV1().StorageClusters(cluster.Namespace).Update(context.TODO(), cluster, metav1.UpdateOptions{}) 47 | } 48 | 49 | // GetStorageCluster gets the StorageCluster with given name and namespace 50 | func (c *Client) GetStorageCluster(name, namespace string) (*corev1.StorageCluster, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | return c.ost.CoreV1().StorageClusters(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 55 | } 56 | 57 | // ListStorageClusters lists all the StorageClusters 58 | func (c *Client) ListStorageClusters(namespace string) (*corev1.StorageClusterList, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | return c.ost.CoreV1().StorageClusters(namespace).List(context.TODO(), metav1.ListOptions{}) 63 | } 64 | 65 | // DeleteStorageCluster deletes the given StorageCluster 66 | func (c *Client) DeleteStorageCluster(name, namespace string) error { 67 | if err := c.initClient(); err != nil { 68 | return err 69 | } 70 | 71 | // TODO Temporary removing PropagationPolicy: &deleteForegroundPolicy from metav1.DeleteOptions{}, until we figure out the correct policy to use 72 | return c.ost.CoreV1().StorageClusters(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 73 | } 74 | 75 | // UpdateStorageClusterStatus update the status of given StorageCluster 76 | func (c *Client) UpdateStorageClusterStatus(cluster *corev1.StorageCluster) (*corev1.StorageCluster, error) { 77 | if err := c.initClient(); err != nil { 78 | return nil, err 79 | } 80 | return c.ost.CoreV1().StorageClusters(cluster.Namespace).UpdateStatus(context.TODO(), cluster, metav1.UpdateOptions{}) 81 | } 82 | -------------------------------------------------------------------------------- /k8s/operatormarketplace/catalogsource.go: -------------------------------------------------------------------------------- 1 | package operatormarketplace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/operator-framework/api/pkg/operators/v1alpha1" 7 | "k8s.io/apimachinery/pkg/types" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | // CatalogSourceOps is an interface to perfrom k8s CatalogSource operations 12 | type CatalogSourceOps interface { 13 | // CreateCatalogSource creates the given CatalogSource 14 | CreateCatalogSource(*v1alpha1.CatalogSource) (*v1alpha1.CatalogSource, error) 15 | // UpdateCatalogSource updates the given CatalogSource 16 | UpdateCatalogSource(*v1alpha1.CatalogSource) (*v1alpha1.CatalogSource, error) 17 | // GetCatalogSource gets the CatalogSource with given name and namespace 18 | GetCatalogSource(name, namespace string) (*v1alpha1.CatalogSource, error) 19 | // ListCatalogSources lists all CatalogSources in given namespace 20 | ListCatalogSources(namespace string) (*v1alpha1.CatalogSourceList, error) 21 | // DeleteCatalogSource deletes the CatalogSource with given name and namespace 22 | DeleteCatalogSource(name, namespace string) error 23 | } 24 | 25 | // CreateCatalogSource creates the given CatalogSource 26 | func (c *Client) CreateCatalogSource(catalogSource *v1alpha1.CatalogSource) (*v1alpha1.CatalogSource, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | if err := c.crClient.Create(context.TODO(), catalogSource); err != nil { 32 | return nil, err 33 | } 34 | return catalogSource, nil 35 | } 36 | 37 | // UpdateCatalogSource updates the given CatalogSource 38 | func (c *Client) UpdateCatalogSource(catalogSource *v1alpha1.CatalogSource) (*v1alpha1.CatalogSource, error) { 39 | if err := c.initClient(); err != nil { 40 | return nil, err 41 | } 42 | 43 | if err := c.crClient.Update(context.TODO(), catalogSource); err != nil { 44 | return nil, err 45 | } 46 | return catalogSource, nil 47 | } 48 | 49 | // GetCatalogSource gets the CatalogSource with given name and namespace 50 | func (c *Client) GetCatalogSource(name, namespace string) (*v1alpha1.CatalogSource, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | 55 | catalogSource := &v1alpha1.CatalogSource{} 56 | if err := c.crClient.Get( 57 | context.TODO(), 58 | types.NamespacedName{ 59 | Namespace: namespace, 60 | Name: name, 61 | }, 62 | catalogSource, 63 | ); err != nil { 64 | return nil, err 65 | } 66 | return catalogSource, nil 67 | } 68 | 69 | // ListCatalogSources lists all CatalogSources in the given namespace 70 | func (c *Client) ListCatalogSources(namespace string) (*v1alpha1.CatalogSourceList, error) { 71 | if err := c.initClient(); err != nil { 72 | return nil, err 73 | } 74 | 75 | catalogSourceList := &v1alpha1.CatalogSourceList{} 76 | if err := c.crClient.List( 77 | context.TODO(), 78 | catalogSourceList, 79 | &client.ListOptions{ 80 | Namespace: namespace, 81 | }, 82 | ); err != nil { 83 | return catalogSourceList, err 84 | } 85 | return catalogSourceList, nil 86 | } 87 | 88 | // DeleteCatalogSource deletes the CatalogSource with given name and namespace 89 | func (c *Client) DeleteCatalogSource(name, namespace string) error { 90 | if err := c.initClient(); err != nil { 91 | return err 92 | } 93 | 94 | catalogSource := &v1alpha1.CatalogSource{} 95 | catalogSource.Name = name 96 | catalogSource.Namespace = namespace 97 | 98 | if err := c.crClient.Delete(context.TODO(), catalogSource); err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /k8s/operatormarketplace/installplan.go: -------------------------------------------------------------------------------- 1 | package operatormarketplace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/operator-framework/api/pkg/operators/v1alpha1" 7 | "k8s.io/apimachinery/pkg/types" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | // InstallPlanOps is an interface to perfrom k8s InstallPlan operations 12 | type InstallPlanOps interface { 13 | // CreateInstallPlan creates the given InstallPlan 14 | CreateInstallPlan(*v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) 15 | // UpdateInstallPlan updates the given InstallPlan 16 | UpdateInstallPlan(*v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) 17 | // GetInstallPlan gets the InstallPlan with given name and namespace 18 | GetInstallPlan(name, namespace string) (*v1alpha1.InstallPlan, error) 19 | // ListInstallPlans lists all InstallPlans in given namespace 20 | ListInstallPlans(namespace string) (*v1alpha1.InstallPlanList, error) 21 | // DeleteInstallPlan deletes the InstallPlan with given name and namespace 22 | DeleteInstallPlan(name, namespace string) error 23 | } 24 | 25 | // CreateInstallPlan creates the given InstallPlan 26 | func (c *Client) CreateInstallPlan(installPlan *v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | if err := c.crClient.Create(context.TODO(), installPlan); err != nil { 32 | return nil, err 33 | } 34 | return installPlan, nil 35 | } 36 | 37 | // UpdateInstallPlan updates the given InstallPlan 38 | func (c *Client) UpdateInstallPlan(installPlan *v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) { 39 | if err := c.initClient(); err != nil { 40 | return nil, err 41 | } 42 | 43 | if err := c.crClient.Update(context.TODO(), installPlan); err != nil { 44 | return nil, err 45 | } 46 | return installPlan, nil 47 | } 48 | 49 | // GetInstallPlan gets the InstallPlan with given name and namespace 50 | func (c *Client) GetInstallPlan(name, namespace string) (*v1alpha1.InstallPlan, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | 55 | installPlan := &v1alpha1.InstallPlan{} 56 | if err := c.crClient.Get( 57 | context.TODO(), 58 | types.NamespacedName{ 59 | Namespace: namespace, 60 | Name: name, 61 | }, 62 | installPlan, 63 | ); err != nil { 64 | return nil, err 65 | } 66 | return installPlan, nil 67 | } 68 | 69 | // ListInstallPlans lists all InstallPlans in the given namespace 70 | func (c *Client) ListInstallPlans(namespace string) (*v1alpha1.InstallPlanList, error) { 71 | if err := c.initClient(); err != nil { 72 | return nil, err 73 | } 74 | 75 | installPlanList := &v1alpha1.InstallPlanList{} 76 | if err := c.crClient.List( 77 | context.TODO(), 78 | installPlanList, 79 | &client.ListOptions{ 80 | Namespace: namespace, 81 | }, 82 | ); err != nil { 83 | return installPlanList, err 84 | } 85 | return installPlanList, nil 86 | } 87 | 88 | // DeleteInstallPlan deletes the InstallPlan with given name and namespace 89 | func (c *Client) DeleteInstallPlan(name, namespace string) error { 90 | if err := c.initClient(); err != nil { 91 | return err 92 | } 93 | 94 | installPlan := &v1alpha1.InstallPlan{} 95 | installPlan.Name = name 96 | installPlan.Namespace = namespace 97 | 98 | if err := c.crClient.Delete(context.TODO(), installPlan); err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /k8s/operatormarketplace/operatorgroup.go: -------------------------------------------------------------------------------- 1 | package operatormarketplace 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "github.com/operator-framework/api/pkg/operators/v1" 7 | "k8s.io/apimachinery/pkg/types" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | // OperatorGroupOps is an interface to perfrom k8s OperatorGroup operations 12 | type OperatorGroupOps interface { 13 | // CreateOperatorGroup creates the given OperatorGroup 14 | CreateOperatorGroup(*v1.OperatorGroup) (*v1.OperatorGroup, error) 15 | // UpdateOperatorGroup updates the given OperatorGroup 16 | UpdateOperatorGroup(*v1.OperatorGroup) (*v1.OperatorGroup, error) 17 | // GetOperatorGroup gets the OperatorGroup with given name and namespace 18 | GetOperatorGroup(name, namespace string) (*v1.OperatorGroup, error) 19 | // ListOperatorGroups lists all OperatorGroups in given namespace 20 | ListOperatorGroups(namespace string) (*v1.OperatorGroupList, error) 21 | // DeleteOperatorGroup deletes the OperatorGroup with given name and namespace 22 | DeleteOperatorGroup(name, namespace string) error 23 | } 24 | 25 | // CreateOperatorGroup creates the given OperatorGroup 26 | func (c *Client) CreateOperatorGroup(operatorGroup *v1.OperatorGroup) (*v1.OperatorGroup, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | if err := c.crClient.Create(context.TODO(), operatorGroup); err != nil { 32 | return nil, err 33 | } 34 | return operatorGroup, nil 35 | } 36 | 37 | // UpdateOperatorGroup updates the given OperatorGroup 38 | func (c *Client) UpdateOperatorGroup(operatorGroup *v1.OperatorGroup) (*v1.OperatorGroup, error) { 39 | if err := c.initClient(); err != nil { 40 | return nil, err 41 | } 42 | 43 | if err := c.crClient.Update(context.TODO(), operatorGroup); err != nil { 44 | return nil, err 45 | } 46 | return operatorGroup, nil 47 | } 48 | 49 | // GetOperatorGroup gets the OperatorGroup with given name and namespace 50 | func (c *Client) GetOperatorGroup(name, namespace string) (*v1.OperatorGroup, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | 55 | operatorGroup := &v1.OperatorGroup{} 56 | if err := c.crClient.Get( 57 | context.TODO(), 58 | types.NamespacedName{ 59 | Namespace: namespace, 60 | Name: name, 61 | }, 62 | operatorGroup, 63 | ); err != nil { 64 | return nil, err 65 | } 66 | return operatorGroup, nil 67 | } 68 | 69 | // ListOperatorGroups lists all OperatorGroups in the given namespace 70 | func (c *Client) ListOperatorGroups(namespace string) (*v1.OperatorGroupList, error) { 71 | if err := c.initClient(); err != nil { 72 | return nil, err 73 | } 74 | 75 | operatorGroupList := &v1.OperatorGroupList{} 76 | if err := c.crClient.List( 77 | context.TODO(), 78 | operatorGroupList, 79 | &client.ListOptions{ 80 | Namespace: namespace, 81 | }, 82 | ); err != nil { 83 | return operatorGroupList, err 84 | } 85 | return operatorGroupList, nil 86 | } 87 | 88 | // DeleteOperatorGroup deletes the OperatorGroup with given name and namespace 89 | func (c *Client) DeleteOperatorGroup(name, namespace string) error { 90 | if err := c.initClient(); err != nil { 91 | return err 92 | } 93 | 94 | operatorGroup := &v1.OperatorGroup{} 95 | operatorGroup.Name = name 96 | operatorGroup.Namespace = namespace 97 | 98 | if err := c.crClient.Delete(context.TODO(), operatorGroup); err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /k8s/operatormarketplace/operatormarketplace.go: -------------------------------------------------------------------------------- 1 | package operatormarketplace 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | ofv1 "github.com/operator-framework/api/pkg/operators/v1" 8 | ofv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 9 | "github.com/portworx/sched-ops/k8s/common" 10 | "k8s.io/client-go/kubernetes/scheme" 11 | "k8s.io/client-go/rest" 12 | "k8s.io/client-go/tools/clientcmd" 13 | "sigs.k8s.io/controller-runtime/pkg/client" 14 | "sigs.k8s.io/controller-runtime/pkg/client/config" 15 | ) 16 | 17 | var ( 18 | instance Ops 19 | once sync.Once 20 | ) 21 | 22 | // Ops is an interface to Operator operations. 23 | type Ops interface { 24 | CatalogSourceOps 25 | ClusterServiceVersionOps 26 | OperatorGroupOps 27 | SubscriptionOps 28 | InstallPlanOps 29 | 30 | // SetConfig sets the config and resets the client 31 | SetConfig(config *rest.Config) 32 | } 33 | 34 | // Instance returns a singleton instance of the client. 35 | func Instance() Ops { 36 | once.Do(func() { 37 | if instance == nil { 38 | instance = &Client{} 39 | } 40 | }) 41 | return instance 42 | } 43 | 44 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 45 | func SetInstance(i Ops) { 46 | instance = i 47 | } 48 | 49 | // New builds a new operator client. 50 | func New(crClient client.Client) *Client { 51 | return &Client{ 52 | crClient: crClient, 53 | } 54 | } 55 | 56 | // NewInstanceFromConfigFile returns new instance of client by using given 57 | // config file 58 | func NewInstanceFromConfigFile(config string) (Ops, error) { 59 | newInstance := &Client{} 60 | err := newInstance.loadClientFromKubeconfig(config) 61 | if err != nil { 62 | return nil, err 63 | } 64 | return newInstance, nil 65 | } 66 | 67 | // Client is a wrapper for the operator client. 68 | type Client struct { 69 | config *rest.Config 70 | crClient client.Client 71 | } 72 | 73 | // SetConfig sets the config and resets the client 74 | func (c *Client) SetConfig(cfg *rest.Config) { 75 | c.config = cfg 76 | c.crClient = nil 77 | } 78 | 79 | // initClient the k8s client if uninitialized 80 | func (c *Client) initClient() error { 81 | if c.crClient != nil { 82 | return nil 83 | } 84 | 85 | return c.setClient() 86 | } 87 | 88 | // setClient instantiates a client. 89 | func (c *Client) setClient() error { 90 | if c.config == nil { 91 | c.config = config.GetConfigOrDie() 92 | } 93 | 94 | err := common.SetRateLimiter(c.config) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | s := scheme.Scheme 100 | if err := ofv1alpha1.AddToScheme(s); err != nil { 101 | return err 102 | } 103 | if err := ofv1.AddToScheme(s); err != nil { 104 | return err 105 | } 106 | c.crClient, err = client.New(c.config, client.Options{Scheme: s}) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 115 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 116 | if err != nil { 117 | return err 118 | } 119 | 120 | c.config = config 121 | return c.loadClient() 122 | } 123 | 124 | func (c *Client) loadClient() error { 125 | if c.config == nil { 126 | return fmt.Errorf("rest config is not provided") 127 | } 128 | 129 | err := common.SetRateLimiter(c.config) 130 | if err != nil { 131 | return err 132 | } 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /k8s/operatormarketplace/subscription.go: -------------------------------------------------------------------------------- 1 | package operatormarketplace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/operator-framework/api/pkg/operators/v1alpha1" 7 | "k8s.io/apimachinery/pkg/types" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | // SubscriptionOps is an interface to perfrom k8s Subscription operations 12 | type SubscriptionOps interface { 13 | // CreateSubscription creates the given Subscription 14 | CreateSubscription(*v1alpha1.Subscription) (*v1alpha1.Subscription, error) 15 | // UpdateSubscription updates the given Subscription 16 | UpdateSubscription(*v1alpha1.Subscription) (*v1alpha1.Subscription, error) 17 | // GetSubscription gets the Subscription with given name and namespace 18 | GetSubscription(name, namespace string) (*v1alpha1.Subscription, error) 19 | // ListSubscriptions lists all Subscriptions in given namespace 20 | ListSubscriptions(namespace string) (*v1alpha1.SubscriptionList, error) 21 | // DeleteSubscription deletes the Subscription with given name and namespace 22 | DeleteSubscription(name, namespace string) error 23 | } 24 | 25 | // CreateSubscription creates the given Subscription 26 | func (c *Client) CreateSubscription(subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | if err := c.crClient.Create(context.TODO(), subscription); err != nil { 32 | return nil, err 33 | } 34 | return subscription, nil 35 | } 36 | 37 | // UpdateSubscription updates the given Subscription 38 | func (c *Client) UpdateSubscription(subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) { 39 | if err := c.initClient(); err != nil { 40 | return nil, err 41 | } 42 | 43 | if err := c.crClient.Update(context.TODO(), subscription); err != nil { 44 | return nil, err 45 | } 46 | return subscription, nil 47 | } 48 | 49 | // GetSubscription gets the Subscription with given name and namespace 50 | func (c *Client) GetSubscription(name, namespace string) (*v1alpha1.Subscription, error) { 51 | if err := c.initClient(); err != nil { 52 | return nil, err 53 | } 54 | 55 | subscription := &v1alpha1.Subscription{} 56 | if err := c.crClient.Get( 57 | context.TODO(), 58 | types.NamespacedName{ 59 | Namespace: namespace, 60 | Name: name, 61 | }, 62 | subscription, 63 | ); err != nil { 64 | return nil, err 65 | } 66 | return subscription, nil 67 | } 68 | 69 | // ListSubscriptions lists all Subscriptions in the given namespace 70 | func (c *Client) ListSubscriptions(namespace string) (*v1alpha1.SubscriptionList, error) { 71 | if err := c.initClient(); err != nil { 72 | return nil, err 73 | } 74 | 75 | subscriptionList := &v1alpha1.SubscriptionList{} 76 | if err := c.crClient.List( 77 | context.TODO(), 78 | subscriptionList, 79 | &client.ListOptions{ 80 | Namespace: namespace, 81 | }, 82 | ); err != nil { 83 | return subscriptionList, err 84 | } 85 | return subscriptionList, nil 86 | } 87 | 88 | // DeleteSubscription deletes the Subscription with given name and namespace 89 | func (c *Client) DeleteSubscription(name, namespace string) error { 90 | if err := c.initClient(); err != nil { 91 | return err 92 | } 93 | 94 | subscription := &v1alpha1.Subscription{} 95 | subscription.Name = name 96 | subscription.Namespace = namespace 97 | 98 | if err := c.crClient.Delete(context.TODO(), subscription); err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /k8s/policy/poddisruptionbudget.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | import ( 4 | "context" 5 | 6 | policyv1 "k8s.io/api/policy/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // PodDisruptionBudgetOps is an interface to perform k8s Pod Disruption Budget operations 11 | type PodDisruptionBudgetOps interface { 12 | // CreatePodDisruptionBudget creates the given pod disruption budget 13 | CreatePodDisruptionBudget(policy *policyv1.PodDisruptionBudget) (*policyv1.PodDisruptionBudget, error) 14 | // GetPodDisruptionBudget gets the given pod disruption budget 15 | GetPodDisruptionBudget(name, namespace string) (*policyv1.PodDisruptionBudget, error) 16 | // ListPodDisruptionBudget lists the pod disruption budgets 17 | ListPodDisruptionBudget(namespace string) (*policyv1.PodDisruptionBudgetList, error) 18 | // UpdatePodDisruptionBudget updates the given pod disruption budget 19 | UpdatePodDisruptionBudget(policy *policyv1.PodDisruptionBudget) (*policyv1.PodDisruptionBudget, error) 20 | // DeletePodDisruptionBudget deletes the given pod disruption budget 21 | DeletePodDisruptionBudget(name, namespace string) error 22 | } 23 | 24 | // CreatePodDisruptionBudget creates the given pod disruption budget 25 | func (c *Client) CreatePodDisruptionBudget(podDisruptionBudget *policyv1.PodDisruptionBudget) (*policyv1.PodDisruptionBudget, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.client.PolicyV1().PodDisruptionBudgets(podDisruptionBudget.Namespace).Create(context.TODO(), podDisruptionBudget, metav1.CreateOptions{}) 31 | } 32 | 33 | // GetPodDisruptionBudget gets the given pod disruption budget 34 | func (c *Client) GetPodDisruptionBudget(name, namespace string) (*policyv1.PodDisruptionBudget, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.client.PolicyV1().PodDisruptionBudgets(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 40 | } 41 | 42 | // ListPodDisruptionBudget gets the given pod disruption budget 43 | func (c *Client) ListPodDisruptionBudget(namespace string) (*policyv1.PodDisruptionBudgetList, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.client.PolicyV1().PodDisruptionBudgets(namespace).List(context.TODO(), metav1.ListOptions{}) 49 | } 50 | 51 | // UpdatePodDisruptionBudget updates the given pod disruption budget 52 | func (c *Client) UpdatePodDisruptionBudget(podDisruptionBudget *policyv1.PodDisruptionBudget) (*policyv1.PodDisruptionBudget, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | 57 | return c.client.PolicyV1().PodDisruptionBudgets(podDisruptionBudget.Namespace).Update(context.TODO(), podDisruptionBudget, metav1.UpdateOptions{}) 58 | } 59 | 60 | // DeletePodDisruptionBudget deletes the given pod disruption budget 61 | func (c *Client) DeletePodDisruptionBudget(name, namespace string) error { 62 | if err := c.initClient(); err != nil { 63 | return err 64 | } 65 | 66 | return c.client.PolicyV1().PodDisruptionBudgets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 67 | } 68 | -------------------------------------------------------------------------------- /k8s/policy/poddisruptionbudget_v1beta1.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | import ( 4 | "context" 5 | 6 | policyv1beta1 "k8s.io/api/policy/v1beta1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // PodDisruptionBudgetV1Beta1Ops is an interface to perform k8s Pod Disruption Budget operations 11 | type PodDisruptionBudgetV1Beta1Ops interface { 12 | // CreatePodDisruptionBudgetV1beta1 creates the given pod disruption budget 13 | CreatePodDisruptionBudgetV1beta1(policy *policyv1beta1.PodDisruptionBudget) (*policyv1beta1.PodDisruptionBudget, error) 14 | // GetPodDisruptionBudgetV1beta1 gets the given pod disruption budget 15 | GetPodDisruptionBudgetV1beta1(name, namespace string) (*policyv1beta1.PodDisruptionBudget, error) 16 | // ListPodDisruptionBudgetV1beta1 lists the pod disruption budgets 17 | ListPodDisruptionBudgetV1beta1(namespace string) (*policyv1beta1.PodDisruptionBudgetList, error) 18 | // UpdatePodDisruptionBudgetV1beta1 updates the given pod disruption budget 19 | UpdatePodDisruptionBudgetV1beta1(policy *policyv1beta1.PodDisruptionBudget) (*policyv1beta1.PodDisruptionBudget, error) 20 | // DeletePodDisruptionBudgetV1beta1 deletes the given pod disruption budget 21 | DeletePodDisruptionBudgetV1beta1(name, namespace string) error 22 | } 23 | 24 | // CreatePodDisruptionBudgetV1beta1 creates the given pod disruption budget 25 | func (c *Client) CreatePodDisruptionBudgetV1beta1(podDisruptionBudget *policyv1beta1.PodDisruptionBudget) (*policyv1beta1.PodDisruptionBudget, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.client.PolicyV1beta1().PodDisruptionBudgets(podDisruptionBudget.Namespace).Create(context.TODO(), podDisruptionBudget, metav1.CreateOptions{}) 31 | } 32 | 33 | // GetPodDisruptionBudgetV1beta1 gets the given pod disruption budget 34 | func (c *Client) GetPodDisruptionBudgetV1beta1(name, namespace string) (*policyv1beta1.PodDisruptionBudget, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.client.PolicyV1beta1().PodDisruptionBudgets(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 40 | } 41 | 42 | // ListPodDisruptionBudgetV1beta1 gets the given pod disruption budget 43 | func (c *Client) ListPodDisruptionBudgetV1beta1(namespace string) (*policyv1beta1.PodDisruptionBudgetList, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.client.PolicyV1beta1().PodDisruptionBudgets(namespace).List(context.TODO(), metav1.ListOptions{}) 49 | } 50 | 51 | // UpdatePodDisruptionBudgetV1beta1 updates the given pod disruption budget 52 | func (c *Client) UpdatePodDisruptionBudgetV1beta1(podDisruptionBudget *policyv1beta1.PodDisruptionBudget) (*policyv1beta1.PodDisruptionBudget, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | 57 | return c.client.PolicyV1beta1().PodDisruptionBudgets(podDisruptionBudget.Namespace).Update(context.TODO(), podDisruptionBudget, metav1.UpdateOptions{}) 58 | } 59 | 60 | // DeletePodDisruptionBudgetV1beta1 deletes the given pod disruption budget 61 | func (c *Client) DeletePodDisruptionBudgetV1beta1(name, namespace string) error { 62 | if err := c.initClient(); err != nil { 63 | return err 64 | } 65 | 66 | return c.client.PolicyV1beta1().PodDisruptionBudgets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 67 | } 68 | -------------------------------------------------------------------------------- /k8s/policy/podsecuritypolicy.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | import ( 4 | "context" 5 | 6 | policyv1beta1 "k8s.io/api/policy/v1beta1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // PodSecurityPolicyOps is an interface to perform k8s Pod Security Policy operations 11 | type PodSecurityPolicyOps interface { 12 | // CreatePodSecurityPolicy creates the given pod security policy 13 | CreatePodSecurityPolicy(policy *policyv1beta1.PodSecurityPolicy) (*policyv1beta1.PodSecurityPolicy, error) 14 | // GetPodSecurityPolicy gets the given pod security policy 15 | GetPodSecurityPolicy(name string) (*policyv1beta1.PodSecurityPolicy, error) 16 | // ListPodSecurityPolicies list pods security policies 17 | ListPodSecurityPolicies() (*policyv1beta1.PodSecurityPolicyList, error) 18 | // UpdatePodSecurityPolicy updates the give pod security policy 19 | UpdatePodSecurityPolicy(policy *policyv1beta1.PodSecurityPolicy) (*policyv1beta1.PodSecurityPolicy, error) 20 | // DeletePodSecurityPolicy deletes the given pod security policy 21 | DeletePodSecurityPolicy(name string) error 22 | } 23 | 24 | // CreatePodSecurityPolicy creates the given pod security policy 25 | func (c *Client) CreatePodSecurityPolicy(policy *policyv1beta1.PodSecurityPolicy) (*policyv1beta1.PodSecurityPolicy, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.client.PolicyV1beta1().PodSecurityPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}) 31 | } 32 | 33 | // GetPodSecurityPolicy gets the given pod security policy 34 | func (c *Client) GetPodSecurityPolicy(name string) (*policyv1beta1.PodSecurityPolicy, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.client.PolicyV1beta1().PodSecurityPolicies().Get(context.TODO(), name, metav1.GetOptions{}) 40 | } 41 | 42 | // ListPodSecurityPolicies gets the given pod security policy 43 | func (c *Client) ListPodSecurityPolicies() (*policyv1beta1.PodSecurityPolicyList, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.client.PolicyV1beta1().PodSecurityPolicies().List(context.TODO(), metav1.ListOptions{}) 49 | } 50 | 51 | // UpdatePodSecurityPolicy updates the give pod security policy 52 | func (c *Client) UpdatePodSecurityPolicy(policy *policyv1beta1.PodSecurityPolicy) (*policyv1beta1.PodSecurityPolicy, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | 57 | return c.client.PolicyV1beta1().PodSecurityPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{}) 58 | } 59 | 60 | // DeletePodSecurityPolicy deletes the given pod security policy 61 | func (c *Client) DeletePodSecurityPolicy(name string) error { 62 | if err := c.initClient(); err != nil { 63 | return err 64 | } 65 | 66 | return c.client.PolicyV1beta1().PodSecurityPolicies().Delete(context.TODO(), name, metav1.DeleteOptions{}) 67 | } 68 | -------------------------------------------------------------------------------- /k8s/policy/policy.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/portworx/sched-ops/k8s/common" 9 | "k8s.io/client-go/kubernetes" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops is an interface to perform kubernetes related operations on the policy resources. 20 | type Ops interface { 21 | PodSecurityPolicyOps 22 | PodDisruptionBudgetOps 23 | PodDisruptionBudgetV1Beta1Ops 24 | 25 | // SetConfig sets the config and resets the client 26 | SetConfig(config *rest.Config) 27 | } 28 | 29 | // Instance returns a singleton instance of the client. 30 | func Instance() Ops { 31 | once.Do(func() { 32 | if instance == nil { 33 | instance = &Client{} 34 | } 35 | }) 36 | return instance 37 | } 38 | 39 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 40 | func SetInstance(i Ops) { 41 | instance = i 42 | } 43 | 44 | // New builds a new policy client. 45 | func New(client kubernetes.Interface) *Client { 46 | return &Client{ 47 | client: client, 48 | } 49 | } 50 | 51 | // NewForConfig builds a new policy client for the given config. 52 | func NewForConfig(c *rest.Config) (*Client, error) { 53 | client, err := kubernetes.NewForConfig(c) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return &Client{ 59 | client: client, 60 | }, nil 61 | } 62 | 63 | // NewInstanceFromConfigFile returns new instance of client by using given 64 | // config file 65 | func NewInstanceFromConfigFile(config string) (Ops, error) { 66 | newInstance := &Client{} 67 | err := newInstance.loadClientFromKubeconfig(config) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return newInstance, nil 72 | } 73 | 74 | // Client is a wrapper for the kubernetes policy client. 75 | type Client struct { 76 | config *rest.Config 77 | client kubernetes.Interface 78 | } 79 | 80 | // SetConfig sets the config and resets the client 81 | func (c *Client) SetConfig(cfg *rest.Config) { 82 | c.config = cfg 83 | c.client = nil 84 | } 85 | 86 | // initClient the k8s client if uninitialized 87 | func (c *Client) initClient() error { 88 | if c.client != nil { 89 | return nil 90 | } 91 | 92 | return c.setClient() 93 | } 94 | 95 | // setClient instantiates a client. 96 | func (c *Client) setClient() error { 97 | var err error 98 | 99 | if c.config != nil { 100 | err = c.loadClient() 101 | } else { 102 | kubeconfig := os.Getenv("KUBECONFIG") 103 | if len(kubeconfig) > 0 { 104 | err = c.loadClientFromKubeconfig(kubeconfig) 105 | } else { 106 | err = c.loadClientFromServiceAccount() 107 | } 108 | 109 | } 110 | 111 | return err 112 | } 113 | 114 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 115 | func (c *Client) loadClientFromServiceAccount() error { 116 | config, err := rest.InClusterConfig() 117 | if err != nil { 118 | return err 119 | } 120 | 121 | c.config = config 122 | return c.loadClient() 123 | } 124 | 125 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 126 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | c.config = config 132 | return c.loadClient() 133 | } 134 | 135 | func (c *Client) loadClient() error { 136 | if c.config == nil { 137 | return fmt.Errorf("rest config is not provided") 138 | } 139 | 140 | var err error 141 | err = common.SetRateLimiter(c.config) 142 | if err != nil { 143 | return err 144 | } 145 | c.client, err = kubernetes.NewForConfig(c.config) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | return nil 151 | } 152 | -------------------------------------------------------------------------------- /k8s/prometheus/alertmanager.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "context" 5 | 6 | monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // AlertManagerOps is an interface to perform AlertManager operations 12 | type AlertManagerOps interface { 13 | // ListAlertManagers lists all alertmanager instances in a given namespace 14 | ListAlertManagers(namespace string) (*monitoringv1.AlertmanagerList, error) 15 | // GetAlertManager gets the alert manager that matches the given name 16 | GetAlertManager(name, namespace string) (*monitoringv1.Alertmanager, error) 17 | // CreateAlertManager creates the given alert manager 18 | CreateAlertManager(*monitoringv1.Alertmanager) (*monitoringv1.Alertmanager, error) 19 | // UpdateAlertManager updates the given alert manager 20 | UpdateAlertManager(*monitoringv1.Alertmanager) (*monitoringv1.Alertmanager, error) 21 | // DeleteAlertManager deletes the given alert manager 22 | DeleteAlertManager(name, namespace string) error 23 | } 24 | 25 | // ListAlertManagers lists all alertmanager instances in a given namespace 26 | func (c *Client) ListAlertManagers(namespace string) (*monitoringv1.AlertmanagerList, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | return c.prometheus.MonitoringV1().Alertmanagers(namespace).List(context.TODO(), metav1.ListOptions{}) 32 | } 33 | 34 | // GetAlertManager gets the alert manager that matches the given name 35 | func (c *Client) GetAlertManager(name string, namespace string) (*monitoringv1.Alertmanager, error) { 36 | if err := c.initClient(); err != nil { 37 | return nil, err 38 | } 39 | 40 | return c.prometheus.MonitoringV1().Alertmanagers(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // CreateAlertManager creates the given alert manager 44 | func (c *Client) CreateAlertManager(alertmanager *monitoringv1.Alertmanager) (*monitoringv1.Alertmanager, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | 49 | ns := alertmanager.Namespace 50 | if len(ns) == 0 { 51 | ns = corev1.NamespaceDefault 52 | } 53 | 54 | return c.prometheus.MonitoringV1().Alertmanagers(ns).Create(context.TODO(), alertmanager, metav1.CreateOptions{}) 55 | } 56 | 57 | // UpdateAlertManager updates the given alert manager 58 | func (c *Client) UpdateAlertManager(alertmanager *monitoringv1.Alertmanager) (*monitoringv1.Alertmanager, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | 63 | return c.prometheus.MonitoringV1().Alertmanagers(alertmanager.Namespace).Update(context.TODO(), alertmanager, metav1.UpdateOptions{}) 64 | } 65 | 66 | // DeleteAlertManager deletes the given alert manager 67 | func (c *Client) DeleteAlertManager(name, namespace string) error { 68 | if err := c.initClient(); err != nil { 69 | return err 70 | } 71 | 72 | return c.prometheus.MonitoringV1().Alertmanagers(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 73 | PropagationPolicy: &deleteForegroundPolicy, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /k8s/prometheus/prometheus_test.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | // TestInstance tests the Instance function 10 | func TestInstance(t *testing.T) { 11 | Instance() 12 | 13 | require.NotNil(t, instance, "instance should be initialized") 14 | } 15 | -------------------------------------------------------------------------------- /k8s/prometheus/prometheuspods.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "context" 5 | 6 | monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // PodOps is an interface to perform Prometheus operations 12 | type PodOps interface { 13 | // ListPrometheuses lists all prometheus instances in a given namespace 14 | ListPrometheuses(namespace string) (*monitoringv1.PrometheusList, error) 15 | // GetPrometheus gets the prometheus instance that matches the given name 16 | GetPrometheus(name, namespace string) (*monitoringv1.Prometheus, error) 17 | // CreatePrometheus creates the given prometheus 18 | CreatePrometheus(*monitoringv1.Prometheus) (*monitoringv1.Prometheus, error) 19 | // UpdatePrometheus updates the given prometheus 20 | UpdatePrometheus(*monitoringv1.Prometheus) (*monitoringv1.Prometheus, error) 21 | // DeletePrometheus deletes the given prometheus 22 | DeletePrometheus(name, namespace string) error 23 | } 24 | 25 | // ListPrometheuses lists all prometheus instances in a given namespace 26 | func (c *Client) ListPrometheuses(namespace string) (*monitoringv1.PrometheusList, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | return c.prometheus.MonitoringV1().Prometheuses(namespace).List(context.TODO(), metav1.ListOptions{}) 32 | } 33 | 34 | // GetPrometheus gets the prometheus instance that matches the given name 35 | func (c *Client) GetPrometheus(name string, namespace string) (*monitoringv1.Prometheus, error) { 36 | if err := c.initClient(); err != nil { 37 | return nil, err 38 | } 39 | 40 | return c.prometheus.MonitoringV1().Prometheuses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // CreatePrometheus creates the given prometheus 44 | func (c *Client) CreatePrometheus(prometheus *monitoringv1.Prometheus) (*monitoringv1.Prometheus, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | 49 | ns := prometheus.Namespace 50 | if len(ns) == 0 { 51 | ns = corev1.NamespaceDefault 52 | } 53 | 54 | return c.prometheus.MonitoringV1().Prometheuses(ns).Create(context.TODO(), prometheus, metav1.CreateOptions{}) 55 | } 56 | 57 | // UpdatePrometheus updates the given prometheus 58 | func (c *Client) UpdatePrometheus(prometheus *monitoringv1.Prometheus) (*monitoringv1.Prometheus, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | 63 | return c.prometheus.MonitoringV1().Prometheuses(prometheus.Namespace).Update(context.TODO(), prometheus, metav1.UpdateOptions{}) 64 | } 65 | 66 | // DeletePrometheus deletes the given prometheus 67 | func (c *Client) DeletePrometheus(name, namespace string) error { 68 | if err := c.initClient(); err != nil { 69 | return err 70 | } 71 | 72 | return c.prometheus.MonitoringV1().Prometheuses(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 73 | PropagationPolicy: &deleteForegroundPolicy, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /k8s/prometheus/prometheusrules.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "context" 5 | 6 | monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // RuleOps is an interface to perform PrometheusRule operations 12 | type RuleOps interface { 13 | // ListPrometheusRules creates the given prometheus rule 14 | ListPrometheusRules(namespace string) (*monitoringv1.PrometheusRuleList, error) 15 | // GetPrometheusRule gets the prometheus rule that matches the given name 16 | GetPrometheusRule(name, namespace string) (*monitoringv1.PrometheusRule, error) 17 | // CreatePrometheusRule creates the given prometheus rule 18 | CreatePrometheusRule(*monitoringv1.PrometheusRule) (*monitoringv1.PrometheusRule, error) 19 | // UpdatePrometheusRule updates the given prometheus rule 20 | UpdatePrometheusRule(*monitoringv1.PrometheusRule) (*monitoringv1.PrometheusRule, error) 21 | // DeletePrometheusRule deletes the given prometheus rule 22 | DeletePrometheusRule(name, namespace string) error 23 | } 24 | 25 | // ListPrometheusRules creates the given prometheus rule 26 | func (c *Client) ListPrometheusRules(namespace string) (*monitoringv1.PrometheusRuleList, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | return c.prometheus.MonitoringV1().PrometheusRules(namespace).List(context.TODO(), metav1.ListOptions{}) 32 | } 33 | 34 | // GetPrometheusRule gets the prometheus rule that matches the given name 35 | func (c *Client) GetPrometheusRule(name string, namespace string) (*monitoringv1.PrometheusRule, error) { 36 | if err := c.initClient(); err != nil { 37 | return nil, err 38 | } 39 | 40 | return c.prometheus.MonitoringV1().PrometheusRules(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // CreatePrometheusRule creates the given prometheus rule 44 | func (c *Client) CreatePrometheusRule(rule *monitoringv1.PrometheusRule) (*monitoringv1.PrometheusRule, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | 49 | ns := rule.Namespace 50 | if len(ns) == 0 { 51 | ns = corev1.NamespaceDefault 52 | } 53 | 54 | return c.prometheus.MonitoringV1().PrometheusRules(ns).Create(context.TODO(), rule, metav1.CreateOptions{}) 55 | } 56 | 57 | // UpdatePrometheusRule updates the given prometheus rule 58 | func (c *Client) UpdatePrometheusRule(rule *monitoringv1.PrometheusRule) (*monitoringv1.PrometheusRule, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | 63 | return c.prometheus.MonitoringV1().PrometheusRules(rule.Namespace).Update(context.TODO(), rule, metav1.UpdateOptions{}) 64 | } 65 | 66 | // DeletePrometheusRule deletes the given prometheus rule 67 | func (c *Client) DeletePrometheusRule(name, namespace string) error { 68 | if err := c.initClient(); err != nil { 69 | return err 70 | } 71 | 72 | return c.prometheus.MonitoringV1().PrometheusRules(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 73 | PropagationPolicy: &deleteForegroundPolicy, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /k8s/prometheus/servicemonitor.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "context" 5 | 6 | monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // ServiceMonitorOps is an interface to perform ServiceMonitor operations 12 | type ServiceMonitorOps interface { 13 | // ListServiceMonitors lists all servicemonitors in a given namespace 14 | ListServiceMonitors(namespace string) (*monitoringv1.ServiceMonitorList, error) 15 | // GetServiceMonitor gets the service monitor instance that matches the given name 16 | GetServiceMonitor(name, namespace string) (*monitoringv1.ServiceMonitor, error) 17 | // CreateServiceMonitor creates the given service monitor 18 | CreateServiceMonitor(*monitoringv1.ServiceMonitor) (*monitoringv1.ServiceMonitor, error) 19 | // UpdateServiceMonitor updates the given service monitor 20 | UpdateServiceMonitor(*monitoringv1.ServiceMonitor) (*monitoringv1.ServiceMonitor, error) 21 | // DeleteServiceMonitor deletes the given service monitor 22 | DeleteServiceMonitor(name, namespace string) error 23 | } 24 | 25 | // ListServiceMonitors lists all servicemonitors in a given namespace 26 | func (c *Client) ListServiceMonitors(namespace string) (*monitoringv1.ServiceMonitorList, error) { 27 | if err := c.initClient(); err != nil { 28 | return nil, err 29 | } 30 | 31 | return c.prometheus.MonitoringV1().ServiceMonitors(namespace).List(context.TODO(), metav1.ListOptions{}) 32 | } 33 | 34 | // GetServiceMonitor gets the service monitor instance that matches the given name 35 | func (c *Client) GetServiceMonitor(name string, namespace string) (*monitoringv1.ServiceMonitor, error) { 36 | if err := c.initClient(); err != nil { 37 | return nil, err 38 | } 39 | 40 | return c.prometheus.MonitoringV1().ServiceMonitors(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 41 | } 42 | 43 | // CreateServiceMonitor creates the given service monitor 44 | func (c *Client) CreateServiceMonitor(serviceMonitor *monitoringv1.ServiceMonitor) (*monitoringv1.ServiceMonitor, error) { 45 | if err := c.initClient(); err != nil { 46 | return nil, err 47 | } 48 | 49 | ns := serviceMonitor.Namespace 50 | if len(ns) == 0 { 51 | ns = corev1.NamespaceDefault 52 | } 53 | 54 | return c.prometheus.MonitoringV1().ServiceMonitors(ns).Create(context.TODO(), serviceMonitor, metav1.CreateOptions{}) 55 | } 56 | 57 | // UpdateServiceMonitor updates the given service monitor 58 | func (c *Client) UpdateServiceMonitor(serviceMonitor *monitoringv1.ServiceMonitor) (*monitoringv1.ServiceMonitor, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | 63 | return c.prometheus.MonitoringV1().ServiceMonitors(serviceMonitor.Namespace).Update(context.TODO(), serviceMonitor, metav1.UpdateOptions{}) 64 | } 65 | 66 | // DeleteServiceMonitor deletes the given service monitor 67 | func (c *Client) DeleteServiceMonitor(name, namespace string) error { 68 | if err := c.initClient(); err != nil { 69 | return err 70 | } 71 | 72 | return c.prometheus.MonitoringV1().ServiceMonitors(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 73 | PropagationPolicy: &deleteForegroundPolicy, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /k8s/rbac/clusterrolebindings.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "context" 5 | 6 | rbacv1 "k8s.io/api/rbac/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ClusterRoleBindingOps is an interface to perform operations on ClusterRoleBinding resources. 11 | type ClusterRoleBindingOps interface { 12 | // GetClusterRoleBinding gets the given cluster role binding 13 | GetClusterRoleBinding(name string) (*rbacv1.ClusterRoleBinding, error) 14 | // ListClusterRoleBindings lists the cluster role bindings 15 | ListClusterRoleBindings() (*rbacv1.ClusterRoleBindingList, error) 16 | // CreateClusterRoleBinding creates the given cluster role binding 17 | CreateClusterRoleBinding(role *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) 18 | // UpdateClusterRoleBinding updates the given cluster role binding 19 | UpdateClusterRoleBinding(role *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) 20 | // DeleteClusterRoleBinding deletes the given cluster role binding 21 | DeleteClusterRoleBinding(roleName string) error 22 | } 23 | 24 | // GetClusterRoleBinding gets the given cluster role binding 25 | func (c *Client) GetClusterRoleBinding(name string) (*rbacv1.ClusterRoleBinding, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.rbac.ClusterRoleBindings().Get(context.TODO(), name, metav1.GetOptions{}) 31 | } 32 | 33 | // ListClusterRoleBindings lists the cluster role bindings 34 | func (c *Client) ListClusterRoleBindings() (*rbacv1.ClusterRoleBindingList, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.rbac.ClusterRoleBindings().List(context.TODO(), metav1.ListOptions{}) 40 | } 41 | 42 | // CreateClusterRoleBinding creates the given cluster role binding 43 | func (c *Client) CreateClusterRoleBinding(binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.rbac.ClusterRoleBindings().Create(context.TODO(), binding, metav1.CreateOptions{}) 49 | } 50 | 51 | // UpdateClusterRoleBinding updates the given cluster role binding 52 | func (c *Client) UpdateClusterRoleBinding(binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | 57 | return c.rbac.ClusterRoleBindings().Update(context.TODO(), binding, metav1.UpdateOptions{}) 58 | } 59 | 60 | // DeleteClusterRoleBinding deletes the given cluster role binding 61 | func (c *Client) DeleteClusterRoleBinding(bindingName string) error { 62 | if err := c.initClient(); err != nil { 63 | return err 64 | } 65 | 66 | return c.rbac.ClusterRoleBindings().Delete(context.TODO(), bindingName, metav1.DeleteOptions{ 67 | PropagationPolicy: &deleteForegroundPolicy, 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /k8s/rbac/clusterroles.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "context" 5 | 6 | rbacv1 "k8s.io/api/rbac/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ClusterRoleOps is an interface to perform operations on ClusterRole resources. 11 | type ClusterRoleOps interface { 12 | // CreateClusterRole creates the given cluster role 13 | CreateClusterRole(role *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) 14 | // GetClusterRole gets the given cluster role 15 | GetClusterRole(name string) (*rbacv1.ClusterRole, error) 16 | // UpdateClusterRole updates the given cluster role 17 | UpdateClusterRole(role *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) 18 | // DeleteClusterRole deletes the given cluster role 19 | DeleteClusterRole(roleName string) error 20 | } 21 | 22 | // CreateClusterRole creates the given cluster role 23 | func (c *Client) CreateClusterRole(role *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | 28 | return c.rbac.ClusterRoles().Create(context.TODO(), role, metav1.CreateOptions{}) 29 | } 30 | 31 | // GetClusterRole gets the given cluster role 32 | func (c *Client) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { 33 | if err := c.initClient(); err != nil { 34 | return nil, err 35 | } 36 | 37 | return c.rbac.ClusterRoles().Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // UpdateClusterRole updates the given cluster role 41 | func (c *Client) UpdateClusterRole(role *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | 46 | return c.rbac.ClusterRoles().Update(context.TODO(), role, metav1.UpdateOptions{}) 47 | } 48 | 49 | // DeleteClusterRole deletes the given cluster role 50 | func (c *Client) DeleteClusterRole(roleName string) error { 51 | if err := c.initClient(); err != nil { 52 | return err 53 | } 54 | 55 | return c.rbac.ClusterRoles().Delete(context.TODO(), roleName, metav1.DeleteOptions{ 56 | PropagationPolicy: &deleteForegroundPolicy, 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /k8s/rbac/rbac.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/portworx/sched-ops/k8s/common" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1" 11 | "k8s.io/client-go/rest" 12 | "k8s.io/client-go/tools/clientcmd" 13 | ) 14 | 15 | var ( 16 | instance Ops 17 | once sync.Once 18 | 19 | deleteForegroundPolicy = metav1.DeletePropagationForeground 20 | ) 21 | 22 | // Ops is an interface to perform kubernetes related operations on the rbac resources. 23 | type Ops interface { 24 | ClusterRoleBindingOps 25 | ClusterRoleOps 26 | RoleBindingOps 27 | RoleOps 28 | 29 | // SetConfig sets the config and resets the client 30 | SetConfig(config *rest.Config) 31 | } 32 | 33 | // Instance returns a singleton instance of the client. 34 | func Instance() Ops { 35 | once.Do(func() { 36 | if instance == nil { 37 | instance = &Client{} 38 | } 39 | }) 40 | return instance 41 | } 42 | 43 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 44 | func SetInstance(i Ops) { 45 | instance = i 46 | } 47 | 48 | // New builds a new rbac client. 49 | func New(client rbacv1client.RbacV1Interface) *Client { 50 | return &Client{ 51 | rbac: client, 52 | } 53 | } 54 | 55 | // NewForConfig builds a new rbac client for the given config. 56 | func NewForConfig(c *rest.Config) (*Client, error) { 57 | rbac, err := rbacv1client.NewForConfig(c) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | return &Client{ 63 | rbac: rbac, 64 | }, nil 65 | } 66 | 67 | // NewInstanceFromConfigFile returns new instance of client by using given 68 | // config file 69 | func NewInstanceFromConfigFile(config string) (Ops, error) { 70 | newInstance := &Client{} 71 | err := newInstance.loadClientFromKubeconfig(config) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return newInstance, nil 76 | } 77 | 78 | // Client is a wrapper for the kubernetes rbac client. 79 | type Client struct { 80 | config *rest.Config 81 | rbac rbacv1client.RbacV1Interface 82 | } 83 | 84 | // SetConfig sets the config and resets the client 85 | func (c *Client) SetConfig(cfg *rest.Config) { 86 | c.config = cfg 87 | c.rbac = nil 88 | } 89 | 90 | // initClient the k8s client if uninitialized 91 | func (c *Client) initClient() error { 92 | if c.rbac != nil { 93 | return nil 94 | } 95 | 96 | return c.setClient() 97 | } 98 | 99 | // setClient instantiates a client. 100 | func (c *Client) setClient() error { 101 | var err error 102 | 103 | if c.config != nil { 104 | err = c.loadClient() 105 | } else { 106 | kubeconfig := os.Getenv("KUBECONFIG") 107 | if len(kubeconfig) > 0 { 108 | err = c.loadClientFromKubeconfig(kubeconfig) 109 | } else { 110 | err = c.loadClientFromServiceAccount() 111 | } 112 | 113 | } 114 | 115 | return err 116 | } 117 | 118 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 119 | func (c *Client) loadClientFromServiceAccount() error { 120 | config, err := rest.InClusterConfig() 121 | if err != nil { 122 | return err 123 | } 124 | 125 | c.config = config 126 | return c.loadClient() 127 | } 128 | 129 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 130 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | c.config = config 136 | return c.loadClient() 137 | } 138 | 139 | func (c *Client) loadClient() error { 140 | if c.config == nil { 141 | return fmt.Errorf("rest config is not provided") 142 | } 143 | 144 | var err error 145 | err = common.SetRateLimiter(c.config) 146 | if err != nil { 147 | return err 148 | } 149 | c.rbac, err = rbacv1client.NewForConfig(c.config) 150 | if err != nil { 151 | return err 152 | } 153 | 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /k8s/rbac/rbac_test.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/rbac/rolebindings.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "context" 5 | 6 | rbacv1 "k8s.io/api/rbac/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // RoleBindingOps is an interface to perform operations on RoleBinding resources. 11 | type RoleBindingOps interface { 12 | // CreateRoleBinding creates the given role binding 13 | CreateRoleBinding(role *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) 14 | // UpdateRoleBinding updates the given role binding 15 | UpdateRoleBinding(role *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) 16 | // GetRoleBinding gets the given role binding 17 | GetRoleBinding(name, namespace string) (*rbacv1.RoleBinding, error) 18 | // DeleteRoleBinding deletes the given role binding 19 | DeleteRoleBinding(name, namespace string) error 20 | // ListRoleBinding returns the list of role bindings 21 | ListRoleBinding(namespace string, filterOptions metav1.ListOptions) (*rbacv1.RoleBindingList, error) 22 | } 23 | 24 | // CreateRoleBinding creates the given role binding 25 | func (c *Client) CreateRoleBinding(binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.rbac.RoleBindings(binding.Namespace).Create(context.TODO(), binding, metav1.CreateOptions{}) 31 | } 32 | 33 | // UpdateRoleBinding updates the given role binding 34 | func (c *Client) UpdateRoleBinding(binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.rbac.RoleBindings(binding.Namespace).Update(context.TODO(), binding, metav1.UpdateOptions{}) 40 | } 41 | 42 | // GetRoleBinding gets the given role binding 43 | func (c *Client) GetRoleBinding(name, namespace string) (*rbacv1.RoleBinding, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.rbac.RoleBindings(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 49 | } 50 | 51 | // DeleteRoleBinding deletes the given role binding 52 | func (c *Client) DeleteRoleBinding(name, namespace string) error { 53 | if err := c.initClient(); err != nil { 54 | return err 55 | } 56 | 57 | return c.rbac.RoleBindings(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 58 | PropagationPolicy: &deleteForegroundPolicy, 59 | }) 60 | } 61 | 62 | // ListRoleBinding returns the list of role bindings 63 | func (c *Client) ListRoleBinding(namespace string, filterOptions metav1.ListOptions) (*rbacv1.RoleBindingList, error) { 64 | if err := c.initClient(); err != nil { 65 | return nil, err 66 | } 67 | 68 | return c.rbac.RoleBindings(namespace).List(context.TODO(), filterOptions) 69 | } 70 | -------------------------------------------------------------------------------- /k8s/rbac/roles.go: -------------------------------------------------------------------------------- 1 | package rbac 2 | 3 | import ( 4 | "context" 5 | 6 | rbac_v1 "k8s.io/api/rbac/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // RoleOps is an interface to perform operations on role resources. 11 | type RoleOps interface { 12 | // CreateRole creates the given role 13 | CreateRole(role *rbac_v1.Role) (*rbac_v1.Role, error) 14 | // UpdateRole updates the given role 15 | UpdateRole(role *rbac_v1.Role) (*rbac_v1.Role, error) 16 | // GetRole gets the given role 17 | GetRole(name, namespace string) (*rbac_v1.Role, error) 18 | // DeleteRole deletes the given role 19 | DeleteRole(name, namespace string) error 20 | // ListRoles returns the list of roles 21 | ListRoles(namespace string, filterOptions metav1.ListOptions) (*rbac_v1.RoleList, error) 22 | } 23 | 24 | // CreateRole creates the given role 25 | func (c *Client) CreateRole(role *rbac_v1.Role) (*rbac_v1.Role, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.rbac.Roles(role.Namespace).Create(context.TODO(), role, metav1.CreateOptions{}) 31 | } 32 | 33 | // UpdateRole updates the given role 34 | func (c *Client) UpdateRole(role *rbac_v1.Role) (*rbac_v1.Role, error) { 35 | if err := c.initClient(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return c.rbac.Roles(role.Namespace).Update(context.TODO(), role, metav1.UpdateOptions{}) 40 | } 41 | 42 | // GetRole gets the given role 43 | func (c *Client) GetRole(name, namespace string) (*rbac_v1.Role, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.rbac.Roles(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 49 | } 50 | 51 | // DeleteRole deletes the given role 52 | func (c *Client) DeleteRole(name, namespace string) error { 53 | if err := c.initClient(); err != nil { 54 | return err 55 | } 56 | 57 | return c.rbac.Roles(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 58 | PropagationPolicy: &deleteForegroundPolicy, 59 | }) 60 | } 61 | 62 | // ListRoles returns the list of roles 63 | func (c *Client) ListRoles(namespace string, filterOptions metav1.ListOptions) (*rbac_v1.RoleList, error) { 64 | if err := c.initClient(); err != nil { 65 | return nil, err 66 | } 67 | 68 | return c.rbac.Roles(namespace).List(context.TODO(), filterOptions) 69 | } 70 | -------------------------------------------------------------------------------- /k8s/storage/csidriver.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "context" 5 | 6 | storagev1 "k8s.io/api/storage/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // CsiDriverOps is an interface to perform k8s CsiDriver readonly operations 11 | type CsiDriverOps interface { 12 | // ListCsiDrivers lists all CSI drivers 13 | ListCsiDrivers() (*storagev1.CSIDriverList, error) 14 | // GetCsiDriver returns the CSI driver for the given name 15 | GetCsiDriver(name string) (*storagev1.CSIDriver, error) 16 | } 17 | 18 | // ListCsiDrivers lists all CSI drivers 19 | func (c *Client) ListCsiDrivers() (*storagev1.CSIDriverList, error) { 20 | if err := c.initClient(); err != nil { 21 | return nil, err 22 | } 23 | 24 | return c.storage.CSIDrivers().List(context.TODO(), metav1.ListOptions{}) 25 | } 26 | 27 | // GetCsiDriver returns the CSI driver for the given name 28 | func (c *Client) GetCsiDriver(name string) (*storagev1.CSIDriver, error) { 29 | if err := c.initClient(); err != nil { 30 | return nil, err 31 | } 32 | 33 | return c.storage.CSIDrivers().Get(context.TODO(), name, metav1.GetOptions{}) 34 | } 35 | -------------------------------------------------------------------------------- /k8s/storage/storage.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/portworx/sched-ops/k8s/common" 9 | storagev1client "k8s.io/client-go/kubernetes/typed/storage/v1" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var ( 15 | instance Ops 16 | once sync.Once 17 | ) 18 | 19 | // Ops is an interface to perform kubernetes related operations on the core resources. 20 | type Ops interface { 21 | ScOps 22 | VolumeAttachmentOps 23 | CsiDriverOps 24 | 25 | // SetConfig sets the config and resets the client 26 | SetConfig(config *rest.Config) 27 | } 28 | 29 | // Instance returns a singleton instance of the client. 30 | func Instance() Ops { 31 | once.Do(func() { 32 | if instance == nil { 33 | instance = &Client{} 34 | } 35 | }) 36 | return instance 37 | } 38 | 39 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 40 | func SetInstance(i Ops) { 41 | instance = i 42 | } 43 | 44 | // New creates a new client. 45 | func New(storage storagev1client.StorageV1Interface) *Client { 46 | return &Client{ 47 | storage: storage, 48 | } 49 | } 50 | 51 | // NewForConfig creates a new client for the given config. 52 | func NewForConfig(c *rest.Config) (*Client, error) { 53 | storage, err := storagev1client.NewForConfig(c) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return &Client{ 59 | storage: storage, 60 | }, nil 61 | } 62 | 63 | // NewInstanceFromConfigFile returns new instance of client by using given 64 | // config file 65 | func NewInstanceFromConfigFile(config string) (Ops, error) { 66 | newInstance := &Client{} 67 | err := newInstance.loadClientFromKubeconfig(config) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return newInstance, nil 72 | } 73 | 74 | // Client is a wrapper for the kubernetes storage client. 75 | type Client struct { 76 | config *rest.Config 77 | storage storagev1client.StorageV1Interface 78 | } 79 | 80 | // SetConfig sets the config and resets the client 81 | func (c *Client) SetConfig(cfg *rest.Config) { 82 | c.config = cfg 83 | c.storage = nil 84 | } 85 | 86 | // initClient the k8s client if uninitialized 87 | func (c *Client) initClient() error { 88 | if c.storage != nil { 89 | return nil 90 | } 91 | 92 | return c.setClient() 93 | } 94 | 95 | // setClient instantiates a client. 96 | func (c *Client) setClient() error { 97 | var err error 98 | 99 | if c.config != nil { 100 | err = c.loadClient() 101 | } else { 102 | kubeconfig := os.Getenv("KUBECONFIG") 103 | if len(kubeconfig) > 0 { 104 | err = c.loadClientFromKubeconfig(kubeconfig) 105 | } else { 106 | err = c.loadClientFromServiceAccount() 107 | } 108 | 109 | } 110 | 111 | return err 112 | } 113 | 114 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 115 | func (c *Client) loadClientFromServiceAccount() error { 116 | config, err := rest.InClusterConfig() 117 | if err != nil { 118 | return err 119 | } 120 | 121 | c.config = config 122 | return c.loadClient() 123 | } 124 | 125 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 126 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | c.config = config 132 | return c.loadClient() 133 | } 134 | 135 | func (c *Client) loadClient() error { 136 | if c.config == nil { 137 | return fmt.Errorf("rest config is not provided") 138 | } 139 | 140 | var err error 141 | err = common.SetRateLimiter(c.config) 142 | if err != nil { 143 | return err 144 | } 145 | c.storage, err = storagev1client.NewForConfig(c.config) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | return nil 151 | } 152 | -------------------------------------------------------------------------------- /k8s/storage/storage_test.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/storage/volumeattachments.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "context" 5 | 6 | storagev1 "k8s.io/api/storage/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // VolumeAttachmentOps is an interface to perform k8s VolumeAttachmentOps operations 11 | type VolumeAttachmentOps interface { 12 | // ListVolumeAttachments lists all volume attachments 13 | ListVolumeAttachments() (*storagev1.VolumeAttachmentList, error) 14 | // DeleteVolumeAttachment deletes a given Volume Attachment by name 15 | DeleteVolumeAttachment(name string) error 16 | // CreateVolumeAttachment creates a volume attachment 17 | CreateVolumeAttachment(*storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) 18 | // UpdateVolumeAttachment updates a volume attachment 19 | UpdateVolumeAttachment(*storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) 20 | // UpdateVolumeAttachmentStatus updates a volume attachment status 21 | UpdateVolumeAttachmentStatus(*storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) 22 | } 23 | 24 | // ListVolumeAttachments lists all volume attachments 25 | func (c *Client) ListVolumeAttachments() (*storagev1.VolumeAttachmentList, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | 30 | return c.storage.VolumeAttachments().List(context.TODO(), metav1.ListOptions{}) 31 | } 32 | 33 | // DeleteVolumeAttachment deletes a given Volume Attachment by name 34 | func (c *Client) DeleteVolumeAttachment(name string) error { 35 | if err := c.initClient(); err != nil { 36 | return err 37 | } 38 | 39 | return c.storage.VolumeAttachments().Delete(context.TODO(), name, metav1.DeleteOptions{}) 40 | } 41 | 42 | // CreateVolumeAttachment creates a volume attachment 43 | func (c *Client) CreateVolumeAttachment(volumeAttachment *storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) { 44 | if err := c.initClient(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return c.storage.VolumeAttachments().Create(context.TODO(), volumeAttachment, metav1.CreateOptions{}) 49 | } 50 | 51 | // UpdateVolumeAttachment updates a volume attachment 52 | func (c *Client) UpdateVolumeAttachment(volumeAttachment *storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) { 53 | if err := c.initClient(); err != nil { 54 | return nil, err 55 | } 56 | 57 | return c.storage.VolumeAttachments().Update(context.TODO(), volumeAttachment, metav1.UpdateOptions{}) 58 | } 59 | 60 | // UpdateVolumeAttachmentStatus updates a volume attachment status 61 | func (c *Client) UpdateVolumeAttachmentStatus(volumeAttachment *storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) { 62 | if err := c.initClient(); err != nil { 63 | return nil, err 64 | } 65 | 66 | return c.storage.VolumeAttachments().UpdateStatus(context.TODO(), volumeAttachment, metav1.UpdateOptions{}) 67 | } 68 | -------------------------------------------------------------------------------- /k8s/stork/action.go: -------------------------------------------------------------------------------- 1 | package stork 2 | 3 | import ( 4 | "context" 5 | 6 | storkv1alpha1 "github.com/libopenstorage/stork/pkg/apis/stork/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // ActionOps is an interface to manage Action Object 11 | type ActionOps interface { 12 | // CreateAction creates a Action 13 | CreateAction(*storkv1alpha1.Action) (*storkv1alpha1.Action, error) 14 | // GetAction gets the Action 15 | GetAction(string, string) (*storkv1alpha1.Action, error) 16 | // ListActions lists all the Actions 17 | ListActions(namespace string) (*storkv1alpha1.ActionList, error) 18 | // UpdateAction updates the Action 19 | UpdateAction(*storkv1alpha1.Action) (*storkv1alpha1.Action, error) 20 | // DeleteAction deletes the Action 21 | DeleteAction(string, string) error 22 | } 23 | 24 | // CreateAction creates a Action 25 | func (c *Client) CreateAction(action *storkv1alpha1.Action) (*storkv1alpha1.Action, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.stork.StorkV1alpha1().Actions(action.Namespace).Create(context.TODO(), action, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetAction gets the Action 33 | func (c *Client) GetAction(name string, namespace string) (*storkv1alpha1.Action, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.stork.StorkV1alpha1().Actions(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListActions lists all the Actions 41 | func (c *Client) ListActions(namespace string) (*storkv1alpha1.ActionList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.stork.StorkV1alpha1().Actions(namespace).List(context.TODO(), metav1.ListOptions{}) 46 | } 47 | 48 | // UpdateAction updates the Action 49 | func (c *Client) UpdateAction(action *storkv1alpha1.Action) (*storkv1alpha1.Action, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.stork.StorkV1alpha1().Actions(action.Namespace).Update(context.TODO(), action, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteAction deletes the Action 57 | func (c *Client) DeleteAction(name string, namespace string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.stork.StorkV1alpha1().Actions(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 62 | PropagationPolicy: &deleteForegroundPolicy, 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/stork/namespacedschedulepolicy.go: -------------------------------------------------------------------------------- 1 | package stork 2 | 3 | import ( 4 | "context" 5 | 6 | storkv1alpha1 "github.com/libopenstorage/stork/pkg/apis/stork/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // NamespacedSchedulePolicyOps is an interface to manage NamespacedSchedulePolicy Object 11 | type NamespacedSchedulePolicyOps interface { 12 | // CreateNamespacedSchedulePolicy creates a NamespacedSchedulePolicy 13 | CreateNamespacedSchedulePolicy(*storkv1alpha1.NamespacedSchedulePolicy) (*storkv1alpha1.NamespacedSchedulePolicy, error) 14 | // GetNamespacedSchedulePolicy gets the NamespacedSchedulePolicy 15 | GetNamespacedSchedulePolicy(string, string) (*storkv1alpha1.NamespacedSchedulePolicy, error) 16 | // ListNamespacedSchedulePolicies lists all the NamespacedSchedulePolicies 17 | ListNamespacedSchedulePolicies(namespace string, filterOptions metav1.ListOptions) (*storkv1alpha1.NamespacedSchedulePolicyList, error) 18 | // UpdateNamespacedSchedulePolicy updates the NamespacedSchedulePolicy 19 | UpdateNamespacedSchedulePolicy(*storkv1alpha1.NamespacedSchedulePolicy) (*storkv1alpha1.NamespacedSchedulePolicy, error) 20 | // DeleteNamespacedSchedulePolicy deletes the NamespacedSchedulePolicy 21 | DeleteNamespacedSchedulePolicy(string, string) error 22 | } 23 | 24 | // CreateNamespacedSchedulePolicy creates a NamespacedSchedulePolicy 25 | func (c *Client) CreateNamespacedSchedulePolicy(schedulePolicy *storkv1alpha1.NamespacedSchedulePolicy) (*storkv1alpha1.NamespacedSchedulePolicy, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.stork.StorkV1alpha1().NamespacedSchedulePolicies(schedulePolicy.Namespace).Create(context.TODO(), schedulePolicy, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetNamespacedSchedulePolicy gets the NamespacedSchedulePolicy 33 | func (c *Client) GetNamespacedSchedulePolicy(name string, namespace string) (*storkv1alpha1.NamespacedSchedulePolicy, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.stork.StorkV1alpha1().NamespacedSchedulePolicies(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListNamespacedSchedulePolicies lists all the NamespacedSchedulePolicies 41 | func (c *Client) ListNamespacedSchedulePolicies(namespace string, filterOptions metav1.ListOptions) (*storkv1alpha1.NamespacedSchedulePolicyList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.stork.StorkV1alpha1().NamespacedSchedulePolicies(namespace).List(context.TODO(), filterOptions) 46 | } 47 | 48 | // UpdateNamespacedSchedulePolicy updates the NamespacedSchedulePolicy 49 | func (c *Client) UpdateNamespacedSchedulePolicy(schedulePolicy *storkv1alpha1.NamespacedSchedulePolicy) (*storkv1alpha1.NamespacedSchedulePolicy, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.stork.StorkV1alpha1().NamespacedSchedulePolicies(schedulePolicy.Namespace).Update(context.TODO(), schedulePolicy, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteNamespacedSchedulePolicy deletes the NamespacedSchedulePolicy 57 | func (c *Client) DeleteNamespacedSchedulePolicy(name string, namespace string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.stork.StorkV1alpha1().NamespacedSchedulePolicies(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 62 | PropagationPolicy: &deleteForegroundPolicy, 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/stork/rule.go: -------------------------------------------------------------------------------- 1 | package stork 2 | 3 | import ( 4 | "context" 5 | 6 | storkv1alpha1 "github.com/libopenstorage/stork/pkg/apis/stork/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // RuleOps is an interface to perform operations for k8s stork rule 11 | type RuleOps interface { 12 | // GetRule fetches the given stork rule 13 | GetRule(name, namespace string) (*storkv1alpha1.Rule, error) 14 | // CreateRule creates the given stork rule 15 | CreateRule(rule *storkv1alpha1.Rule) (*storkv1alpha1.Rule, error) 16 | // UpdateRule updates the given stork rule 17 | UpdateRule(rule *storkv1alpha1.Rule) (*storkv1alpha1.Rule, error) 18 | // DeleteRule deletes the given stork rule 19 | DeleteRule(name, namespace string) error 20 | // ListRules returns the list of rules 21 | ListRules(namespace string, filterOptions metav1.ListOptions) (*storkv1alpha1.RuleList, error) 22 | } 23 | 24 | // GetRule fetches the given stork rule 25 | func (c *Client) GetRule(name, namespace string) (*storkv1alpha1.Rule, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.stork.StorkV1alpha1().Rules(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 30 | } 31 | 32 | // CreateRule creates the given stork rule 33 | func (c *Client) CreateRule(rule *storkv1alpha1.Rule) (*storkv1alpha1.Rule, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.stork.StorkV1alpha1().Rules(rule.GetNamespace()).Create(context.TODO(), rule, metav1.CreateOptions{}) 38 | } 39 | 40 | // UpdateRule updates the given stork rule 41 | func (c *Client) UpdateRule(rule *storkv1alpha1.Rule) (*storkv1alpha1.Rule, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.stork.StorkV1alpha1().Rules(rule.GetNamespace()).Update(context.TODO(), rule, metav1.UpdateOptions{}) 46 | } 47 | 48 | // DeleteRule deletes the given stork rule 49 | func (c *Client) DeleteRule(name, namespace string) error { 50 | if err := c.initClient(); err != nil { 51 | return err 52 | } 53 | return c.stork.StorkV1alpha1().Rules(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{ 54 | PropagationPolicy: &deleteForegroundPolicy, 55 | }) 56 | } 57 | 58 | // ListRules returns the list of rules 59 | func (c *Client) ListRules(namespace string, filterOptions metav1.ListOptions) (*storkv1alpha1.RuleList, error) { 60 | if err := c.initClient(); err != nil { 61 | return nil, err 62 | } 63 | return c.stork.StorkV1alpha1().Rules(namespace).List(context.TODO(), filterOptions) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/stork/schedulepolicy.go: -------------------------------------------------------------------------------- 1 | package stork 2 | 3 | import ( 4 | "context" 5 | 6 | storkv1alpha1 "github.com/libopenstorage/stork/pkg/apis/stork/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // SchedulePolicyOps is an interface to manage SchedulePolicy Object 11 | type SchedulePolicyOps interface { 12 | // CreateSchedulePolicy creates a SchedulePolicy 13 | CreateSchedulePolicy(*storkv1alpha1.SchedulePolicy) (*storkv1alpha1.SchedulePolicy, error) 14 | // GetSchedulePolicy gets the SchedulePolicy 15 | GetSchedulePolicy(string) (*storkv1alpha1.SchedulePolicy, error) 16 | // ListSchedulePolicies lists all the SchedulePolicies 17 | ListSchedulePolicies() (*storkv1alpha1.SchedulePolicyList, error) 18 | // UpdateSchedulePolicy updates the SchedulePolicy 19 | UpdateSchedulePolicy(*storkv1alpha1.SchedulePolicy) (*storkv1alpha1.SchedulePolicy, error) 20 | // DeleteSchedulePolicy deletes the SchedulePolicy 21 | DeleteSchedulePolicy(string) error 22 | } 23 | 24 | // CreateSchedulePolicy creates a SchedulePolicy 25 | func (c *Client) CreateSchedulePolicy(schedulePolicy *storkv1alpha1.SchedulePolicy) (*storkv1alpha1.SchedulePolicy, error) { 26 | if err := c.initClient(); err != nil { 27 | return nil, err 28 | } 29 | return c.stork.StorkV1alpha1().SchedulePolicies().Create(context.TODO(), schedulePolicy, metav1.CreateOptions{}) 30 | } 31 | 32 | // GetSchedulePolicy gets the SchedulePolicy 33 | func (c *Client) GetSchedulePolicy(name string) (*storkv1alpha1.SchedulePolicy, error) { 34 | if err := c.initClient(); err != nil { 35 | return nil, err 36 | } 37 | return c.stork.StorkV1alpha1().SchedulePolicies().Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // ListSchedulePolicies lists all the SchedulePolicies 41 | func (c *Client) ListSchedulePolicies() (*storkv1alpha1.SchedulePolicyList, error) { 42 | if err := c.initClient(); err != nil { 43 | return nil, err 44 | } 45 | return c.stork.StorkV1alpha1().SchedulePolicies().List(context.TODO(), metav1.ListOptions{}) 46 | } 47 | 48 | // UpdateSchedulePolicy updates the SchedulePolicy 49 | func (c *Client) UpdateSchedulePolicy(schedulePolicy *storkv1alpha1.SchedulePolicy) (*storkv1alpha1.SchedulePolicy, error) { 50 | if err := c.initClient(); err != nil { 51 | return nil, err 52 | } 53 | return c.stork.StorkV1alpha1().SchedulePolicies().Update(context.TODO(), schedulePolicy, metav1.UpdateOptions{}) 54 | } 55 | 56 | // DeleteSchedulePolicy deletes the SchedulePolicy 57 | func (c *Client) DeleteSchedulePolicy(name string) error { 58 | if err := c.initClient(); err != nil { 59 | return err 60 | } 61 | return c.stork.StorkV1alpha1().SchedulePolicies().Delete(context.TODO(), name, metav1.DeleteOptions{ 62 | PropagationPolicy: &deleteForegroundPolicy, 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /k8s/stork/stork_test.go: -------------------------------------------------------------------------------- 1 | package stork 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/talisman/talisman.go: -------------------------------------------------------------------------------- 1 | package talisman 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/portworx/sched-ops/k8s/common" 9 | talismanclientset "github.com/portworx/talisman/pkg/client/clientset/versioned" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/client-go/rest" 12 | "k8s.io/client-go/tools/clientcmd" 13 | ) 14 | 15 | var ( 16 | instance Ops 17 | once sync.Once 18 | 19 | deleteForegroundPolicy = metav1.DeletePropagationForeground 20 | ) 21 | 22 | // Ops is an interface to Talisman operations. 23 | type Ops interface { 24 | VolumePlacementStrategyOps 25 | 26 | // SetConfig sets the config and resets the client 27 | SetConfig(config *rest.Config) 28 | } 29 | 30 | // Instance returns a singleton instance of the client. 31 | func Instance() Ops { 32 | once.Do(func() { 33 | if instance == nil { 34 | instance = &Client{} 35 | } 36 | }) 37 | return instance 38 | } 39 | 40 | // SetInstance replaces the instance with the provided one. Should be used only for testing purposes. 41 | func SetInstance(i Ops) { 42 | instance = i 43 | } 44 | 45 | // New builds a new talisman client. 46 | func New(c talismanclientset.Interface) *Client { 47 | return &Client{ 48 | talisman: c, 49 | } 50 | } 51 | 52 | // NewForConfig builds a new talisman client for the given config. 53 | func NewForConfig(c *rest.Config) (*Client, error) { 54 | talismanClient, err := talismanclientset.NewForConfig(c) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | return &Client{ 60 | talisman: talismanClient, 61 | }, nil 62 | } 63 | 64 | // NewInstanceFromConfigFile returns new instance of client by using given 65 | // config file 66 | func NewInstanceFromConfigFile(config string) (Ops, error) { 67 | newInstance := &Client{} 68 | err := newInstance.loadClientFromKubeconfig(config) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return newInstance, nil 73 | } 74 | 75 | // Client is a wrapper for the talisman operator client. 76 | type Client struct { 77 | config *rest.Config 78 | talisman talismanclientset.Interface 79 | } 80 | 81 | // SetConfig sets the config and resets the client 82 | func (c *Client) SetConfig(cfg *rest.Config) { 83 | c.config = cfg 84 | c.talisman = nil 85 | } 86 | 87 | // initClient the k8s client if uninitialized 88 | func (c *Client) initClient() error { 89 | if c.talisman != nil { 90 | return nil 91 | } 92 | 93 | return c.setClient() 94 | } 95 | 96 | // setClient instantiates a client. 97 | func (c *Client) setClient() error { 98 | var err error 99 | 100 | if c.config != nil { 101 | err = c.loadClient() 102 | } else { 103 | kubeconfig := os.Getenv("KUBECONFIG") 104 | if len(kubeconfig) > 0 { 105 | err = c.loadClientFromKubeconfig(kubeconfig) 106 | } else { 107 | err = c.loadClientFromServiceAccount() 108 | } 109 | 110 | } 111 | 112 | return err 113 | } 114 | 115 | // loadClientFromServiceAccount loads a k8s client from a ServiceAccount specified in the pod running px 116 | func (c *Client) loadClientFromServiceAccount() error { 117 | config, err := rest.InClusterConfig() 118 | if err != nil { 119 | return err 120 | } 121 | 122 | c.config = config 123 | return c.loadClient() 124 | } 125 | 126 | func (c *Client) loadClientFromKubeconfig(kubeconfig string) error { 127 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | c.config = config 133 | return c.loadClient() 134 | } 135 | 136 | func (c *Client) loadClient() error { 137 | if c.config == nil { 138 | return fmt.Errorf("rest config is not provided") 139 | } 140 | 141 | var err error 142 | err = common.SetRateLimiter(c.config) 143 | if err != nil { 144 | return err 145 | } 146 | c.talisman, err = talismanclientset.NewForConfig(c.config) 147 | if err != nil { 148 | return err 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /k8s/talisman/talisman_test.go: -------------------------------------------------------------------------------- 1 | package talisman 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestInstance(t *testing.T) { 10 | Instance() 11 | 12 | require.NotNil(t, instance, "instance should be initialized") 13 | } 14 | -------------------------------------------------------------------------------- /k8s/talisman/volumeplacementstrategy.go: -------------------------------------------------------------------------------- 1 | package talisman 2 | 3 | import ( 4 | talismanv1beta2 "github.com/portworx/talisman/pkg/apis/portworx/v1beta2" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // VolumePlacementStrategyOps is an interface to perform CRUD volume placememt strategy ops 9 | type VolumePlacementStrategyOps interface { 10 | // CreateVolumePlacementStrategy creates a new volume placement strategy 11 | CreateVolumePlacementStrategy(spec *talismanv1beta2.VolumePlacementStrategy) (*talismanv1beta2.VolumePlacementStrategy, error) 12 | // UpdateVolumePlacementStrategy updates an existing volume placement strategy 13 | UpdateVolumePlacementStrategy(spec *talismanv1beta2.VolumePlacementStrategy) (*talismanv1beta2.VolumePlacementStrategy, error) 14 | // ListVolumePlacementStrategies lists all volume placement strategies 15 | ListVolumePlacementStrategies() (*talismanv1beta2.VolumePlacementStrategyList, error) 16 | // DeleteVolumePlacementStrategy deletes the volume placement strategy with given name 17 | DeleteVolumePlacementStrategy(name string) error 18 | // GetVolumePlacementStrategy returns the volume placememt strategy with given name 19 | GetVolumePlacementStrategy(name string) (*talismanv1beta2.VolumePlacementStrategy, error) 20 | } 21 | 22 | // CreateVolumePlacementStrategy creates a new volume placement strategy 23 | func (c *Client) CreateVolumePlacementStrategy(spec *talismanv1beta2.VolumePlacementStrategy) (*talismanv1beta2.VolumePlacementStrategy, error) { 24 | if err := c.initClient(); err != nil { 25 | return nil, err 26 | } 27 | return c.talisman.Portworx().VolumePlacementStrategies().Create(spec) 28 | } 29 | 30 | // UpdateVolumePlacementStrategy updates an existing volume placement strategy 31 | func (c *Client) UpdateVolumePlacementStrategy(spec *talismanv1beta2.VolumePlacementStrategy) (*talismanv1beta2.VolumePlacementStrategy, error) { 32 | if err := c.initClient(); err != nil { 33 | return nil, err 34 | } 35 | return c.talisman.Portworx().VolumePlacementStrategies().Update(spec) 36 | } 37 | 38 | // ListVolumePlacementStrategies lists all volume placement strategies 39 | func (c *Client) ListVolumePlacementStrategies() (*talismanv1beta2.VolumePlacementStrategyList, error) { 40 | if err := c.initClient(); err != nil { 41 | return nil, err 42 | } 43 | return c.talisman.Portworx().VolumePlacementStrategies().List(metav1.ListOptions{}) 44 | } 45 | 46 | // DeleteVolumePlacementStrategy deletes the volume placement strategy with given name 47 | func (c *Client) DeleteVolumePlacementStrategy(name string) error { 48 | if err := c.initClient(); err != nil { 49 | return err 50 | } 51 | return c.talisman.Portworx().VolumePlacementStrategies().Delete(name, &metav1.DeleteOptions{ 52 | 53 | PropagationPolicy: &deleteForegroundPolicy, 54 | }) 55 | } 56 | 57 | // GetVolumePlacementStrategy returns the volume placememt strategy with given name 58 | func (c *Client) GetVolumePlacementStrategy(name string) (*talismanv1beta2.VolumePlacementStrategy, error) { 59 | if err := c.initClient(); err != nil { 60 | return nil, err 61 | } 62 | return c.talisman.Portworx().VolumePlacementStrategies().Get(name, metav1.GetOptions{}) 63 | } 64 | -------------------------------------------------------------------------------- /task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | //TODO: export the type: type Task func() (string, error) 12 | 13 | // ErrTimedOut is returned when an operation times out 14 | // Is this type used anywhere? If not we can get rid off it in favor context.DeadlineExceeded 15 | type ErrTimedOut struct { 16 | // Reason is the reason for the timeout 17 | Reason string 18 | } 19 | 20 | func (e *ErrTimedOut) Error() string { 21 | errString := "timed out performing task." 22 | if len(e.Reason) > 0 { 23 | errString = fmt.Sprintf("%s, Error was: %s", errString, e.Reason) 24 | } 25 | 26 | return errString 27 | } 28 | 29 | // DoRetryWithTimeout performs given task with given timeout and timeBeforeRetry 30 | // TODO(stgleb): In future I would like to add context as a first param to this function 31 | // so calling code can cancel task. 32 | func DoRetryWithTimeout(t func() (interface{}, bool, error), timeout, timeBeforeRetry time.Duration) (interface{}, error) { 33 | // Use context.Context as a standard go way of timeout and cancellation propagation amount goroutines. 34 | ctx, cancel := context.WithTimeout(context.Background(), timeout) 35 | defer cancel() 36 | 37 | resultChan := make(chan interface{}) 38 | errChan := make(chan error) 39 | errInRetires := make([]string, 0) 40 | 41 | go func() { 42 | for { 43 | select { 44 | case <-ctx.Done(): 45 | 46 | if ctx.Err() != nil { 47 | errChan <- ctx.Err() 48 | } 49 | 50 | return 51 | default: 52 | out, retry, err := t() 53 | if err != nil { 54 | if retry { 55 | errInRetires = append(errInRetires, err.Error()) 56 | log.Printf("DoRetryWithTimeout - Error: {%v}, Next try in [%v], timeout [%v]", err, timeBeforeRetry, timeout) 57 | time.Sleep(timeBeforeRetry) 58 | } else { 59 | errChan <- err 60 | return 61 | } 62 | } else { 63 | resultChan <- out 64 | return 65 | } 66 | } 67 | } 68 | }() 69 | 70 | select { 71 | case result := <-resultChan: 72 | return result, nil 73 | case err := <-errChan: 74 | if err == context.DeadlineExceeded { 75 | return nil, &ErrTimedOut{ 76 | Reason: fmt.Sprintf("DoRetryWithTimeout timed out. Errors generated in retries: {%s}", strings.Join(errInRetires, "}\n{")), 77 | } 78 | } 79 | 80 | return nil, err 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /task/task_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestDoRetry(t *testing.T) { 12 | t1 := func() (interface{}, bool, error) { 13 | return "hello world", false, nil 14 | } 15 | 16 | output, err := DoRetryWithTimeout(t1, 1*time.Minute, 5*time.Second) 17 | require.NoError(t, err, "failed to run task") 18 | require.NotEmpty(t, output, "ask returned empty output") 19 | 20 | t2 := func() (interface{}, bool, error) { 21 | return "", true, fmt.Errorf("task is failing") 22 | } 23 | 24 | retryTill := time.Now().Add(10 * time.Second) 25 | output, err = DoRetryWithTimeout(t2, 10*time.Second, 2*time.Second) 26 | require.Error(t, err, "task was expected to fail") 27 | require.Empty(t, output, "task should have returned empty output") 28 | require.True(t, time.Now().After(retryTill) || time.Now().Equal(retryTill), "current time should be after expected timeout") 29 | 30 | // tighter retry loop 31 | t3 := func() (interface{}, bool, error) { 32 | return "", true, fmt.Errorf("task is failing") 33 | } 34 | 35 | retryTill = time.Now().Add(2 * time.Second) 36 | output, err = DoRetryWithTimeout(t3, 2*time.Second, 10*time.Millisecond) 37 | require.Error(t, err, "task was expected to fail") 38 | require.Empty(t, output, "task should have returned empty output") 39 | require.True(t, time.Now().After(retryTill) || time.Now().Equal(retryTill), "current time should be after expected timeout") 40 | 41 | } 42 | 43 | func TestDoRetryWithTimeoutSuccessAfter(t *testing.T) { 44 | counter := 0 45 | t4 := func() (interface{}, bool, error) { 46 | 47 | if counter > 3 { 48 | return "", false, nil 49 | } 50 | 51 | counter++ 52 | return nil, true, fmt.Errorf("task is failing") 53 | } 54 | 55 | output, err := DoRetryWithTimeout(t4, 100*time.Millisecond, 10*time.Millisecond) 56 | require.NoError(t, err, "task must not fail") 57 | require.NotNil(t, output, "result must not be nil") 58 | } 59 | 60 | func TestDoRetryWithTimeoutSuccessNilReturn(t *testing.T) { 61 | t4 := func() (interface{}, bool, error) { 62 | t.Log("Hello") 63 | return nil, true, nil 64 | } 65 | 66 | output, err := DoRetryWithTimeout(t4, 100*time.Millisecond, 10*time.Millisecond) 67 | 68 | require.NoError(t, err, "task must not fail") 69 | require.Nil(t, output, "result must not be nil") 70 | } 71 | --------------------------------------------------------------------------------