From ea5ff6a4bd351538941c24a4461aee934327fb56 Mon Sep 17 00:00:00 2001 From: Vivek Singh Date: Fri, 25 Jun 2021 11:55:32 +0530 Subject: [PATCH] Upgrade Kanister CRDs to v1 from v1beta1 (#927) * Add CRDs generated by kubebuilder We tried to use kubebuilder to generate the CRDs for go types but that is also not able to process some of the types, for example `BlueprintPhase.Args`, `Phase.Output` becauaes of `interface{}` and `Blueprint.Actions` because of pointer type value. * Correct CRDs * Have CRDs in the go source files as strings * Remove `phases.output` from required fields * Resolve import cycle, refactor PR - Remove required fields from CRDs - Move CR resource names to consts package to resolve import cycle issue - Remove un-nec logging * Fix integration test failure by changing types.go Even though e2e tests were running on my local cluster tests were failing on the kanister CI, and the reason was, when we read blueprint to create blueprint type if a field is not specified in the blueprint, we initialized the type with null for that field and in that case CRD validation failed. This commit tries to fix that by settign `omitemtpy` for those fields. I am thinking if we should add `omitempty` for every field in types.go * Fix unit tests by adding omitemtpy in types.go * Change actionset CRD to have kopiaSnapshot field * Change kopiaSnapshot type to string in actionset crd * Fix integration test failure * Fix controller unit test by adding `KopiaSnapshot` in actionset status * Fix resource unit test failure In the test `ResourceSuite.TestBlueprintClient` we tried to create blueprint with empty actions struct and then tried to make sure that we are able to update the blueprint. And checked actions after update that its not nil. Since we are omitting empty field we are not going to get actions if we pass empty actions, that why I have changed the action to have some values. * Remove un nec CRD manifest * Update CRDs if CRDs are already present in the cluster Per current implementation we were not silently ignoring the error if the CRDs are already present in the cluster. Since we are significantly changing the CRD that wouldn't work for, so this PR updates the CRDs if they are already avaialable. * Address review comments * Correct permissions of controller Dockerfile Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- pkg/apis/cr/v1alpha1/register.go | 21 +-- pkg/apis/cr/v1alpha1/types.go | 56 ++---- pkg/consts/consts.go | 10 ++ pkg/customresource/actionset-crd.go | 254 +++++++++++++++++++++++++++ pkg/customresource/blueprint-crd.go | 110 ++++++++++++ pkg/customresource/customresource.go | 80 ++++++--- pkg/customresource/profile-crd.go | 111 ++++++++++++ pkg/kancontroller/kancontroller.go | 2 +- pkg/resource/resource_test.go | 7 +- pkg/testutil/testutil.go | 3 +- 10 files changed, 579 insertions(+), 75 deletions(-) create mode 100644 pkg/customresource/actionset-crd.go create mode 100644 pkg/customresource/blueprint-crd.go create mode 100644 pkg/customresource/profile-crd.go diff --git a/pkg/apis/cr/v1alpha1/register.go b/pkg/apis/cr/v1alpha1/register.go index 4408926642..91d8c5bb5a 100644 --- a/pkg/apis/cr/v1alpha1/register.go +++ b/pkg/apis/cr/v1alpha1/register.go @@ -21,11 +21,12 @@ package v1alpha1 import ( "reflect" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/kanisterio/kanister/pkg/consts" customresource "github.com/kanisterio/kanister/pkg/customresource" ) @@ -37,31 +38,31 @@ var ( // ActionSetResource is a CRD for actionsets. var ActionSetResource = customresource.CustomResource{ - Name: ActionSetResourceName, - Plural: ActionSetResourceNamePlural, + Name: consts.ActionSetResourceName, + Plural: consts.ActionSetResourceNamePlural, Group: ResourceGroup, Version: SchemeVersion, - Scope: apiextensionsv1beta1.NamespaceScoped, + Scope: apiextensionsv1.NamespaceScoped, Kind: reflect.TypeOf(ActionSet{}).Name(), } // BlueprintResource is a CRD for blueprints. var BlueprintResource = customresource.CustomResource{ - Name: BlueprintResourceName, - Plural: BlueprintResourceNamePlural, + Name: consts.BlueprintResourceName, + Plural: consts.BlueprintResourceNamePlural, Group: ResourceGroup, Version: SchemeVersion, - Scope: apiextensionsv1beta1.NamespaceScoped, + Scope: apiextensionsv1.NamespaceScoped, Kind: reflect.TypeOf(Blueprint{}).Name(), } // ProfileResource is a CRD for blueprints. var ProfileResource = customresource.CustomResource{ - Name: ProfileResourceName, - Plural: ProfileResourceNamePlural, + Name: consts.ProfileResourceName, + Plural: consts.ProfileResourceNamePlural, Group: ResourceGroup, Version: SchemeVersion, - Scope: apiextensionsv1beta1.NamespaceScoped, + Scope: apiextensionsv1.NamespaceScoped, Kind: reflect.TypeOf(Profile{}).Name(), } diff --git a/pkg/apis/cr/v1alpha1/types.go b/pkg/apis/cr/v1alpha1/types.go index b19fad18bf..6d77fce5fc 100644 --- a/pkg/apis/cr/v1alpha1/types.go +++ b/pkg/apis/cr/v1alpha1/types.go @@ -38,12 +38,6 @@ const ( // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = schema.GroupVersion{Group: ResourceGroup, Version: SchemeVersion} -// These names are used to query ActionSet API objects. -const ( - ActionSetResourceName = "actionset" - ActionSetResourceNamePlural = "actionsets" -) - // JSONMap contains PodOverride specs. type JSONMap sp.JSONMap @@ -57,7 +51,7 @@ var _ runtime.Object = (*ActionSet)(nil) type ActionSet struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Spec *ActionSetSpec `json:"spec"` + Spec *ActionSetSpec `json:"spec,omitempty"` Status *ActionSetStatus `json:"status,omitempty"` } @@ -82,7 +76,7 @@ type ObjectReference struct { // ActionSetSpec is the specification for the actionset. type ActionSetSpec struct { - Actions []ActionSpec `json:"actions"` + Actions []ActionSpec `json:"actions,omitempty"` } // ActionSpec is the specification for a single Action. @@ -96,18 +90,18 @@ type ActionSpec struct { // Artifacts will be passed as inputs into this phase. Artifacts map[string]Artifact `json:"artifacts,omitempty"` // ConfigMaps that we'll get and pass into the blueprint. - ConfigMaps map[string]ObjectReference `json:"configMaps"` + ConfigMaps map[string]ObjectReference `json:"configMaps,omitempty"` // Secrets that we'll get and pass into the blueprint. - Secrets map[string]ObjectReference `json:"secrets"` + Secrets map[string]ObjectReference `json:"secrets,omitempty"` // Profile is use to specify the location where store artifacts and the // credentials authorized to access them. - Profile *ObjectReference `json:"profile"` + Profile *ObjectReference `json:"profile,omitempty"` // PodOverride is used to specify pod specs that will override the // default pod specs PodOverride JSONMap `json:"podOverride,omitempty"` // Options will be used to specify additional values // to be used in the Blueprint. - Options map[string]string `json:"options"` + Options map[string]string `json:"options,omitempty"` // PreferredVersion will be used to select the preferred version of Kanister functions // to be executed for this action PreferredVersion string `json:"preferredVersion"` @@ -116,7 +110,7 @@ type ActionSpec struct { // ActionSetStatus is the status for the actionset. This should only be updated by the controller. type ActionSetStatus struct { State State `json:"state"` - Actions []ActionStatus `json:"actions"` + Actions []ActionStatus `json:"actions,omitempty"` Error Error `json:"error,omitempty"` } @@ -129,9 +123,9 @@ type ActionStatus struct { // Blueprint with instructions on how to execute this action. Blueprint string `json:"blueprint"` // Phases are sub-actions an are executed sequentially. - Phases []Phase `json:"phases"` + Phases []Phase `json:"phases,omitempty"` // Artifacts created by this phase. - Artifacts map[string]Artifact `json:"artifacts"` + Artifacts map[string]Artifact `json:"artifacts,omitempty"` } // State is the current state of a phase of execution. @@ -156,17 +150,17 @@ type Error struct { type Phase struct { Name string `json:"name"` State State `json:"state"` - Output map[string]interface{} `json:"output"` + Output map[string]interface{} `json:"output,omitempty"` } // k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Artifact tracks objects produced by an action. type Artifact struct { - KeyValue map[string]string `json:"keyValue"` + KeyValue map[string]string `json:"keyValue,omitempty"` // KopiaSnapshot captures the kopia snapshot information // produced as a JSON string by kando command in phases of an action. - KopiaSnapshot string `json:"kopiaSnapshot"` + KopiaSnapshot string `json:"kopiaSnapshot,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -178,12 +172,6 @@ type ActionSetList struct { Items []*ActionSet `json:"items"` } -// These names are used to query Blueprint API objects. -const ( - BlueprintResourceName = "blueprint" - BlueprintResourceNamePlural = "blueprints" -) - var _ runtime.Object = (*Blueprint)(nil) // +genclient @@ -194,25 +182,25 @@ var _ runtime.Object = (*Blueprint)(nil) type Blueprint struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Actions map[string]*BlueprintAction `json:"actions"` + Actions map[string]*BlueprintAction `json:"actions,omitempty"` } // BlueprintAction describes the set of phases that constitute an action. type BlueprintAction struct { Name string `json:"name"` Kind string `json:"kind"` - ConfigMapNames []string `json:"configMapNames"` - SecretNames []string `json:"secretNames"` - InputArtifactNames []string `json:"inputArtifactNames"` - OutputArtifacts map[string]Artifact `json:"outputArtifacts"` - Phases []BlueprintPhase `json:"phases"` + ConfigMapNames []string `json:"configMapNames,omitempty"` + SecretNames []string `json:"secretNames,omitempty"` + InputArtifactNames []string `json:"inputArtifactNames,omitempty"` + OutputArtifacts map[string]Artifact `json:"outputArtifacts,omitempty"` + Phases []BlueprintPhase `json:"phases,omitempty"` } // BlueprintPhase is a an individual unit of execution. type BlueprintPhase struct { Func string `json:"func"` Name string `json:"name"` - ObjectRefs map[string]ObjectReference `json:"objects"` + ObjectRefs map[string]ObjectReference `json:"objects,omitempty"` Args map[string]interface{} `json:"args"` } @@ -225,12 +213,6 @@ type BlueprintList struct { Items []*Blueprint `json:"items"` } -// These names are used to query Profile API objects. -const ( - ProfileResourceName = "profile" - ProfileResourceNamePlural = "profiles" -) - // +genclient // +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index 8df2ff0d90..fba2a2b4a8 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -9,3 +9,13 @@ const ( LabelKeyCreatedBy = "createdBy" LabelValueKanister = "kanister" ) + +// These names are used to query ActionSet API objects. +const ( + ActionSetResourceName = "actionset" + ActionSetResourceNamePlural = "actionsets" + BlueprintResourceName = "blueprint" + BlueprintResourceNamePlural = "blueprints" + ProfileResourceName = "profile" + ProfileResourceNamePlural = "profiles" +) diff --git a/pkg/customresource/actionset-crd.go b/pkg/customresource/actionset-crd.go new file mode 100644 index 0000000000..3318434874 --- /dev/null +++ b/pkg/customresource/actionset-crd.go @@ -0,0 +1,254 @@ +package customresource + +const actionsetCRD = ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: actionsets.cr.kanister.io +spec: + group: cr.kanister.io + names: + kind: ActionSet + listKind: ActionSetList + plural: actionsets + singular: actionset + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + actions: + items: + description: ActionSpec is the specification for a single Action. + properties: + artifacts: + additionalProperties: + properties: + keyValue: + additionalProperties: + type: string + type: object + kopiaSnapshot: + type: string + x-kubernetes-preserve-unknown-fields: true + type: object + description: Artifacts will be passed as inputs into this phase. + type: object + blueprint: + description: Blueprint with instructions on how to execute this + action. + type: string + configMaps: + additionalProperties: + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + description: ConfigMaps that we will get and pass into the blueprint. + type: object + name: + description: 'Name is the action we will perform. For example: + backup or restore.' + type: string + object: + description: Object refers to the thing we will perform this action + on. + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + options: + additionalProperties: + type: string + description: Options will be used to specify additional values + to be used in the Blueprint. + type: object + podOverride: + type: object + description: PodOverride is used to specify pod specs that will + override the default pod specs + preferredVersion: + description: PreferredVersion will be used to select the preferred + version of Kanister functions to be executed for this action + type: string + profile: + description: Profile is use to specify the location where store + artifacts and the credentials authorized to access them. + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + secrets: + additionalProperties: + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + description: Secrets that we will get and pass into the blueprint. + type: object + type: object + type: array + type: object + status: + description: ActionSetStatus is the status for the actionset. This should + only be updated by the controller. + properties: + actions: + items: + properties: + artifacts: + additionalProperties: + properties: + keyValue: + additionalProperties: + type: string + type: object + kopiaSnapshot: + type: string + x-kubernetes-preserve-unknown-fields: true + type: object + description: Artifacts created by this phase. + type: object + blueprint: + description: Blueprint with instructions on how to execute this + action. + type: string + name: + description: 'Name is the action we will perform. For example: + backup or restore.' + type: string + object: + description: Object refers to the resource we perform this action + on. + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + phases: + description: Phases are sub-actions an are executed sequentially. + items: + properties: + name: + type: string + output: + x-kubernetes-preserve-unknown-fields: true + type: object + state: + type: string + type: object + type: array + type: object + type: array + error: + properties: + message: + type: string + type: object + state: + type: string + type: object + type: object +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +` diff --git a/pkg/customresource/blueprint-crd.go b/pkg/customresource/blueprint-crd.go new file mode 100644 index 0000000000..416eb14cca --- /dev/null +++ b/pkg/customresource/blueprint-crd.go @@ -0,0 +1,110 @@ +package customresource + +const blueprintCRD = ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: blueprints.cr.kanister.io +spec: + group: cr.kanister.io + names: + kind: Blueprint + listKind: BlueprintList + plural: blueprints + singular: blueprint + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + properties: + actions: + additionalProperties: + properties: + configMapNames: + items: + type: string + type: array + inputArtifactNames: + items: + type: string + type: array + kind: + type: string + name: + type: string + outputArtifacts: + additionalProperties: + properties: + keyValue: + additionalProperties: + type: string + type: object + kopiaSnapshot: + type: string + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + phases: + items: + properties: + args: + x-kubernetes-preserve-unknown-fields: true + type: object + func: + type: string + name: + type: string + objects: + additionalProperties: + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + type: object + type: object + type: array + secretNames: + items: + type: string + type: array + type: object + type: object + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + type: object +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +` diff --git a/pkg/customresource/customresource.go b/pkg/customresource/customresource.go index 9760450589..ad17063262 100644 --- a/pkg/customresource/customresource.go +++ b/pkg/customresource/customresource.go @@ -21,13 +21,17 @@ import ( "time" "github.com/Masterminds/semver" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" + "github.com/kanisterio/kanister/pkg/consts" + // importing go check to bypass the testing flags _ "gopkg.in/check.v1" ) @@ -49,7 +53,7 @@ type CustomResource struct { Version string // Scope of the CRD. Namespaced or cluster - Scope apiextensionsv1beta1.ResourceScope + Scope apiextensionsv1.ResourceScope // Kind is the serialized interface of the resource. Kind string @@ -93,29 +97,57 @@ func CreateCustomResources(context Context, resources []CustomResource) error { return lastErr } -func createCRD(context Context, resource CustomResource) error { - crdName := fmt.Sprintf("%s.%s", resource.Plural, resource.Group) - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: crdName, - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: resource.Group, - Version: resource.Version, - Scope: resource.Scope, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Singular: resource.Name, - Plural: resource.Plural, - Kind: resource.Kind, - }, - }, +func getCRDFromSpec(spec []byte) (*apiextensionsv1.CustomResourceDefinition, error) { + crd := &apiextensionsv1.CustomResourceDefinition{} + if err := decodeSpecIntoObject(spec, crd); err != nil { + return nil, err } + return crd, nil +} - _, err := context.APIExtensionClientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(contextpkg.TODO(), crd, metav1.CreateOptions{}) +func decodeSpecIntoObject(spec []byte, intoObj runtime.Object) error { + d := serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer() + if _, _, err := d.Decode(spec, nil, intoObj); err != nil { + return fmt.Errorf("Failed to decode spec into object: %s; spec %s\n", err.Error(), spec) + } + return nil +} + +func createCRD(context Context, resource CustomResource) error { + crd, err := getCRDFromSpec(specFromResName(resource.Name)) + if err != nil { + return err + } + ctx := contextpkg.Background() + _, err = context.APIExtensionClientset.ApiextensionsV1().CustomResourceDefinitions().Create(ctx, crd, metav1.CreateOptions{}) if err != nil { if !errors.IsAlreadyExists(err) { return fmt.Errorf("failed to create %s CRD. %+v", resource.Name, err) } + + // if CRD already exists, get the resource version and create the CRD with that resource version + c, err := context.APIExtensionClientset.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crd.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("Failed to get CRD to get resource version: %s\n", err.Error()) + } + + crd.ResourceVersion = c.ResourceVersion + _, err = context.APIExtensionClientset.ApiextensionsV1().CustomResourceDefinitions().Update(ctx, crd, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("Failed to delete already present CRD: %s\n", err.Error()) + } + } + return nil +} + +func specFromResName(name string) []byte { + switch name { + case consts.ActionSetResourceName: + return []byte(actionsetCRD) + case consts.BlueprintResourceName: + return []byte(blueprintCRD) + case consts.ProfileResourceName: + return []byte(profileCRD) } return nil } @@ -123,18 +155,18 @@ func createCRD(context Context, resource CustomResource) error { func waitForCRDInit(context Context, resource CustomResource) error { crdName := fmt.Sprintf("%s.%s", resource.Plural, resource.Group) return wait.Poll(context.Interval, context.Timeout, func() (bool, error) { - crd, err := context.APIExtensionClientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(contextpkg.TODO(), crdName, metav1.GetOptions{}) + crd, err := context.APIExtensionClientset.ApiextensionsV1().CustomResourceDefinitions().Get(contextpkg.TODO(), crdName, metav1.GetOptions{}) if err != nil { return false, err } for _, cond := range crd.Status.Conditions { switch cond.Type { - case apiextensionsv1beta1.Established: - if cond.Status == apiextensionsv1beta1.ConditionTrue { + case apiextensionsv1.Established: + if cond.Status == apiextensionsv1.ConditionTrue { return true, nil } - case apiextensionsv1beta1.NamesAccepted: - if cond.Status == apiextensionsv1beta1.ConditionFalse { + case apiextensionsv1.NamesAccepted: + if cond.Status == apiextensionsv1.ConditionFalse { return false, fmt.Errorf("Name conflict: %v ", cond.Reason) } } diff --git a/pkg/customresource/profile-crd.go b/pkg/customresource/profile-crd.go new file mode 100644 index 0000000000..72710b35ec --- /dev/null +++ b/pkg/customresource/profile-crd.go @@ -0,0 +1,111 @@ +package customresource + +const profileCRD = ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: profiles.cr.kanister.io +spec: + group: cr.kanister.io + names: + kind: Profile + listKind: ProfileList + plural: profiles + singular: profile + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + credential: + properties: + keyPair: + properties: + idField: + type: string + secret: + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + secretField: + type: string + type: object + secret: + properties: + apiVersion: + description: API version of the referent. + type: string + group: + description: API Group of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: 'Namespace of the referent. More info: http://kubernetes.io/docs/user-guide/namespaces' + type: string + resource: + description: Resource name of the referent. + type: string + type: object + type: + type: string + type: object + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + location: + properties: + bucket: + type: string + endpoint: + type: string + prefix: + type: string + region: + type: string + type: + type: string + type: object + metadata: + type: object + skipSSLVerify: + type: boolean + type: object +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +` diff --git a/pkg/kancontroller/kancontroller.go b/pkg/kancontroller/kancontroller.go index 2725cf10c1..b440afab30 100644 --- a/pkg/kancontroller/kancontroller.go +++ b/pkg/kancontroller/kancontroller.go @@ -49,7 +49,7 @@ func Execute() { }() go func() { if err := s.ListenAndServe(); err != nil { - log.WithError(err).Print("Failed to shutdown health check server") + log.WithError(err).Print("Failed to start health check server") } }() diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go index 45491110b3..54ad5410e7 100644 --- a/pkg/resource/resource_test.go +++ b/pkg/resource/resource_test.go @@ -124,8 +124,11 @@ func (s *ResourceSuite) TestBlueprintClient(c *C) { bp2, err := cli.Blueprints(s.namespace).Get(ctx, name, emptyGetOptions) c.Assert(err, IsNil) c.Assert(bp1, DeepEquals, bp2) - - bp2.Actions = map[string]*crv1alpha1.BlueprintAction{} + bp2.Actions = map[string]*crv1alpha1.BlueprintAction{ + "backup": { + Name: "takebackup", + }, + } bp3, err := cli.Blueprints(s.namespace).Update(ctx, bp2, metav1.UpdateOptions{}) c.Assert(err, IsNil) c.Assert(bp1.Actions, IsNil) diff --git a/pkg/testutil/testutil.go b/pkg/testutil/testutil.go index b88e58d30c..f6fb20e01d 100644 --- a/pkg/testutil/testutil.go +++ b/pkg/testutil/testutil.go @@ -29,6 +29,7 @@ import ( crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1" awsconfig "github.com/kanisterio/kanister/pkg/aws" "github.com/kanisterio/kanister/pkg/blockstorage" + "github.com/kanisterio/kanister/pkg/consts" ) // NewTestPVC function returns a pointer to a new PVC test object @@ -238,7 +239,7 @@ func NewTestActionSet(namespace, blueprintName, poKind, poName, poNamespace, ver Namespace: poNamespace, }, Profile: &crv1alpha1.ObjectReference{ - Kind: crv1alpha1.ProfileResourceName, + Kind: consts.ProfileResourceName, Name: TestProfileName, Namespace: namespace, },