diff --git a/README.md b/README.md index 0eb5c5735..f7bb4571d 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ For Mac ``` brew install noobaa/noobaa/noobaa # or -wget https://github.com/noobaa/noobaa-operator/releases/download/v2.0.4/noobaa-mac-v2.0.4; mv noobaa-mac-* noobaa; chmod +x noobaa +wget https://github.com/noobaa/noobaa-operator/releases/download/v2.0.6/noobaa-mac-v2.0.6; mv noobaa-mac-* noobaa; chmod +x noobaa ``` For Linux ``` -wget https://github.com/noobaa/noobaa-operator/releases/download/v2.0.4/noobaa-linux-v2.0.4; mv noobaa-linux-* noobaa; chmod +x noobaa +wget https://github.com/noobaa/noobaa-operator/releases/download/v2.0.6/noobaa-linux-v2.0.6; mv noobaa-linux-* noobaa; chmod +x noobaa ``` - Run: `./noobaa --help` for CLI usage diff --git a/deploy/cluster_role.yaml b/deploy/cluster_role.yaml index b55429288..e021b80ce 100644 --- a/deploy/cluster_role.yaml +++ b/deploy/cluster_role.yaml @@ -52,3 +52,15 @@ rules: - get - list - watch +- apiGroups: # from system:auth-delegator + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: # from system:auth-delegator + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/deploy/crds/noobaa_v1alpha1_backingstore_crd.yaml b/deploy/crds/noobaa_v1alpha1_backingstore_crd.yaml index 0b5bbb9f3..c283fd90a 100644 --- a/deploy/crds/noobaa_v1alpha1_backingstore_crd.yaml +++ b/deploy/crds/noobaa_v1alpha1_backingstore_crd.yaml @@ -178,8 +178,6 @@ spec: items: type: object type: array - required: - - phase type: object version: v1alpha1 versions: diff --git a/deploy/crds/noobaa_v1alpha1_bucketclass_crd.yaml b/deploy/crds/noobaa_v1alpha1_bucketclass_crd.yaml index 5594ec810..540ef3ff6 100644 --- a/deploy/crds/noobaa_v1alpha1_bucketclass_crd.yaml +++ b/deploy/crds/noobaa_v1alpha1_bucketclass_crd.yaml @@ -62,6 +62,9 @@ spec: placement: description: Placement specifies the type of placement for the tier If empty it should have a single backing store. + enum: + - Spread + - Mirror type: string type: object type: array @@ -109,8 +112,6 @@ spec: items: type: object type: array - required: - - phase type: object version: v1alpha1 versions: diff --git a/deploy/crds/noobaa_v1alpha1_noobaa_crd.yaml b/deploy/crds/noobaa_v1alpha1_noobaa_crd.yaml index ff01c9114..f18fc7acd 100644 --- a/deploy/crds/noobaa_v1alpha1_noobaa_crd.yaml +++ b/deploy/crds/noobaa_v1alpha1_noobaa_crd.yaml @@ -53,6 +53,9 @@ spec: spec: description: Specification of the desired behavior of the noobaa system. properties: + affinity: + description: Affinity (optional) passed through to noobaa's pods + type: object coreResources: description: CoreResources (optional) overrides the default resource requirements for the server container @@ -106,6 +109,7 @@ spec: description: Most recently observed status of the noobaa system. properties: accounts: + description: Accounts reports accounts info for the admin account properties: admin: properties: @@ -167,6 +171,7 @@ spec: type: object type: array services: + description: Services reports addresses for the services properties: serviceMgmt: properties: @@ -274,13 +279,6 @@ spec: - serviceMgmt - serviceS3 type: object - required: - - observedGeneration - - phase - - actualImage - - accounts - - services - - readme type: object version: v1alpha1 versions: diff --git a/deploy/internal/statefulset-core.yaml b/deploy/internal/statefulset-core.yaml index 09e699e15..a0db14445 100644 --- a/deploy/internal/statefulset-core.yaml +++ b/deploy/internal/statefulset-core.yaml @@ -113,6 +113,10 @@ spec: value: "" - name: OAUTH_TOKEN_ENDPOINT value: "" + - name: NOOBAA_SERVICE_ACCOUNT + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName - name: container_dbg value: "" # any non-empty value will set the container to dbg mode - name: CONTAINER_CPU_REQUEST diff --git a/deploy/operator.yaml b/deploy/operator.yaml index 12eb7df5e..8e9ad763a 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -16,7 +16,7 @@ spec: serviceAccountName: noobaa containers: - name: noobaa-operator - image: noobaa/noobaa-operator:2.0.5 + image: noobaa/noobaa-operator:2.0.6 imagePullPolicy: IfNotPresent resources: limits: diff --git a/deploy/role.yaml b/deploy/role.yaml index dd4f3d297..2a0e487f9 100644 --- a/deploy/role.yaml +++ b/deploy/role.yaml @@ -25,14 +25,7 @@ rules: - events - configmaps - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - serviceaccounts - resourceNames: - - noobaa verbs: - '*' - apiGroups: diff --git a/deploy/service_account.yaml b/deploy/service_account.yaml index 2f22289c9..9c849aa71 100644 --- a/deploy/service_account.yaml +++ b/deploy/service_account.yaml @@ -2,3 +2,5 @@ apiVersion: v1 kind: ServiceAccount metadata: name: noobaa + annotations: + serviceaccounts.openshift.io/oauth-redirectreference.noobaa-mgmt: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"noobaa-mgmt"}}' diff --git a/go.mod b/go.mod index a99c949f2..5418fdceb 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 + go.uber.org/multierr v1.2.0 // indirect golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect golang.org/x/sys v0.0.0-20190904005037-43c01164e931 // indirect diff --git a/go.sum b/go.sum index 20324c323..ba04b43ea 100644 --- a/go.sum +++ b/go.sum @@ -485,6 +485,8 @@ go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4= +go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/pkg/apis/noobaa/v1alpha1/backingstore_types.go b/pkg/apis/noobaa/v1alpha1/backingstore_types.go index 06aa5dd20..6dc460134 100644 --- a/pkg/apis/noobaa/v1alpha1/backingstore_types.go +++ b/pkg/apis/noobaa/v1alpha1/backingstore_types.go @@ -87,7 +87,8 @@ type BackingStoreSpec struct { type BackingStoreStatus struct { // Phase is a simple, high-level summary of where the backing store is in its lifecycle - Phase BackingStorePhase `json:"phase"` + // +optional + Phase BackingStorePhase `json:"phase,omitempty"` // Conditions is a list of conditions related to operator reconciliation // +patchMergeKey=type @@ -96,6 +97,7 @@ type BackingStoreStatus struct { Conditions []conditionsv1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // RelatedObjects is a list of objects related to this operator. + // +optional RelatedObjects []corev1.ObjectReference `json:"relatedObjects,omitempty"` } diff --git a/pkg/apis/noobaa/v1alpha1/bucketclass_types.go b/pkg/apis/noobaa/v1alpha1/bucketclass_types.go index 23dd66531..d14dc9283 100644 --- a/pkg/apis/noobaa/v1alpha1/bucketclass_types.go +++ b/pkg/apis/noobaa/v1alpha1/bucketclass_types.go @@ -65,7 +65,8 @@ type BucketClassSpec struct { // +k8s:openapi-gen=true type BucketClassStatus struct { // Phase is a simple, high-level summary of where the System is in its lifecycle - Phase BucketClassPhase `json:"phase"` + // +optional + Phase BucketClassPhase `json:"phase,omitempty"` // Conditions is a list of conditions related to operator reconciliation // +patchMergeKey=type @@ -74,6 +75,7 @@ type BucketClassStatus struct { Conditions []conditionsv1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // RelatedObjects is a list of objects related to this operator. + // +optional RelatedObjects []corev1.ObjectReference `json:"relatedObjects,omitempty"` } @@ -91,10 +93,13 @@ type Tier struct { // Placement specifies the type of placement for the tier // If empty it should have a single backing store. + // +optional + // +kubebuilder:validation:Enum=Spread,Mirror Placement TierPlacement `json:"placement,omitempty"` // BackingStores is an unordered list of backing store names. // The meaning of the list depends on the placement. + // +optional BackingStores []BackingStoreName `json:"backingStores,omitempty"` } diff --git a/pkg/apis/noobaa/v1alpha1/noobaa_types.go b/pkg/apis/noobaa/v1alpha1/noobaa_types.go index d34cc06f4..0a973232a 100644 --- a/pkg/apis/noobaa/v1alpha1/noobaa_types.go +++ b/pkg/apis/noobaa/v1alpha1/noobaa_types.go @@ -104,6 +104,10 @@ type NooBaaSpec struct { // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + // Affinity (optional) passed through to noobaa's pods + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + // ImagePullSecret (optional) sets a pull secret for the system image // +optional ImagePullSecret *corev1.LocalObjectReference `json:"imagePullSecret,omitempty"` @@ -115,10 +119,12 @@ type NooBaaStatus struct { // ObservedGeneration is the most recent generation observed for this noobaa system. // It corresponds to the CR generation, which is updated on mutation by the API Server. - ObservedGeneration int64 `json:"observedGeneration"` + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` // Phase is a simple, high-level summary of where the System is in its lifecycle - Phase SystemPhase `json:"phase"` + // +optional + Phase SystemPhase `json:"phase,omitempty"` // Conditions is a list of conditions related to operator reconciliation // +patchMergeKey=type @@ -127,17 +133,24 @@ type NooBaaStatus struct { Conditions []conditionsv1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // RelatedObjects is a list of objects related to this operator. + // +optional RelatedObjects []corev1.ObjectReference `json:"relatedObjects,omitempty"` // ActualImage is set to report which image the operator is using - ActualImage string `json:"actualImage"` + // +optional + ActualImage string `json:"actualImage,omitempty"` - Accounts AccountsStatus `json:"accounts"` + // Accounts reports accounts info for the admin account + // +optional + Accounts *AccountsStatus `json:"accounts,omitempty"` - Services ServicesStatus `json:"services"` + // Services reports addresses for the services + // +optional + Services *ServicesStatus `json:"services,omitempty"` // Readme is a user readable string with explanations on the system - Readme string `json:"readme"` + // +optional + Readme string `json:"readme,omitempty"` } // SystemPhase is a string enum type for system phases diff --git a/pkg/apis/noobaa/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/noobaa/v1alpha1/zz_generated.deepcopy.go index faa50ed8d..256c8e81f 100644 --- a/pkg/apis/noobaa/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/noobaa/v1alpha1/zz_generated.deepcopy.go @@ -420,6 +420,11 @@ func (in *NooBaaSpec) DeepCopyInto(out *NooBaaSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(corev1.Affinity) + (*in).DeepCopyInto(*out) + } if in.ImagePullSecret != nil { in, out := &in.ImagePullSecret, &out.ImagePullSecret *out = new(corev1.LocalObjectReference) @@ -453,8 +458,16 @@ func (in *NooBaaStatus) DeepCopyInto(out *NooBaaStatus) { *out = make([]corev1.ObjectReference, len(*in)) copy(*out, *in) } - out.Accounts = in.Accounts - in.Services.DeepCopyInto(&out.Services) + if in.Accounts != nil { + in, out := &in.Accounts, &out.Accounts + *out = new(AccountsStatus) + **out = **in + } + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = new(ServicesStatus) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/noobaa/v1alpha1/zz_generated.openapi.go b/pkg/apis/noobaa/v1alpha1/zz_generated.openapi.go index e6e81845d..37914bcdd 100644 --- a/pkg/apis/noobaa/v1alpha1/zz_generated.openapi.go +++ b/pkg/apis/noobaa/v1alpha1/zz_generated.openapi.go @@ -167,7 +167,6 @@ func schema_pkg_apis_noobaa_v1alpha1_BackingStoreStatus(ref common.ReferenceCall }, }, }, - Required: []string{"phase"}, }, }, Dependencies: []string{ @@ -288,7 +287,6 @@ func schema_pkg_apis_noobaa_v1alpha1_BucketClassStatus(ref common.ReferenceCallb }, }, }, - Required: []string{"phase"}, }, }, Dependencies: []string{ @@ -407,6 +405,12 @@ func schema_pkg_apis_noobaa_v1alpha1_NooBaaSpec(ref common.ReferenceCallback) co }, }, }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "Affinity (optional) passed through to noobaa's pods", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, "imagePullSecret": { SchemaProps: spec.SchemaProps{ Description: "ImagePullSecret (optional) sets a pull secret for the system image", @@ -417,7 +421,7 @@ func schema_pkg_apis_noobaa_v1alpha1_NooBaaSpec(ref common.ReferenceCallback) co }, }, Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration"}, } } @@ -482,12 +486,14 @@ func schema_pkg_apis_noobaa_v1alpha1_NooBaaStatus(ref common.ReferenceCallback) }, "accounts": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/noobaa/noobaa-operator/pkg/apis/noobaa/v1alpha1.AccountsStatus"), + Description: "Accounts reports accounts info for the admin account", + Ref: ref("github.com/noobaa/noobaa-operator/pkg/apis/noobaa/v1alpha1.AccountsStatus"), }, }, "services": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/noobaa/noobaa-operator/pkg/apis/noobaa/v1alpha1.ServicesStatus"), + Description: "Services reports addresses for the services", + Ref: ref("github.com/noobaa/noobaa-operator/pkg/apis/noobaa/v1alpha1.ServicesStatus"), }, }, "readme": { @@ -498,7 +504,6 @@ func schema_pkg_apis_noobaa_v1alpha1_NooBaaStatus(ref common.ReferenceCallback) }, }, }, - Required: []string{"observedGeneration", "phase", "actualImage", "accounts", "services", "readme"}, }, }, Dependencies: []string{ diff --git a/pkg/backingstore/backingstore.go b/pkg/backingstore/backingstore.go index 58e816ff8..15ee1e5f5 100644 --- a/pkg/backingstore/backingstore.go +++ b/pkg/backingstore/backingstore.go @@ -198,9 +198,10 @@ func CmdList() *cobra.Command { // CmdReconcile returns a CLI command func CmdReconcile() *cobra.Command { cmd := &cobra.Command{ - Use: "reconcile", - Short: "Runs a reconcile attempt like noobaa-operator", - Run: RunReconcile, + Hidden: true, + Use: "reconcile", + Short: "Runs a reconcile attempt like noobaa-operator", + Run: RunReconcile, } return cmd } diff --git a/pkg/backingstore/reconciler.go b/pkg/backingstore/reconciler.go index 38d03c9fa..f71416d06 100644 --- a/pkg/backingstore/reconciler.go +++ b/pkg/backingstore/reconciler.go @@ -350,10 +350,6 @@ func (r *Reconciler) ReadSystemInfo() error { } r.SystemInfo = &systemInfo - if r.BackingStore.DeletionTimestamp != nil { - return nil - } - conn, err := r.MakeExternalConnectionParams() if err != nil { return err @@ -511,6 +507,11 @@ func (r *Reconciler) MakeExternalConnectionParams() (*nb.AddExternalConnectionPa fmt.Sprintf("Invalid backing store type %q", r.BackingStore.Spec.Type)) } + if !util.IsStringGraphicCharsOnly(conn.Identity) || !util.IsStringGraphicCharsOnly(conn.Secret) { + return nil, util.NewPersistentError("InvalidSecret", + fmt.Sprintf("Invalid secret containing non graphic characters (perhaps not base64 encoded?) %q", r.Secret.Name)) + } + return conn, nil } diff --git a/pkg/bucketclass/bucketclass.go b/pkg/bucketclass/bucketclass.go index 1bffc7c1f..182bf74ef 100644 --- a/pkg/bucketclass/bucketclass.go +++ b/pkg/bucketclass/bucketclass.go @@ -83,9 +83,10 @@ func CmdList() *cobra.Command { // CmdReconcile returns a CLI command func CmdReconcile() *cobra.Command { cmd := &cobra.Command{ - Use: "reconcile", - Short: "Runs a reconcile attempt like noobaa-operator", - Run: RunReconcile, + Hidden: true, + Use: "reconcile", + Short: "Runs a reconcile attempt like noobaa-operator", + Run: RunReconcile, } return cmd } @@ -101,6 +102,9 @@ func RunCreate(cmd *cobra.Command, args []string) { name := args[0] placement, _ := cmd.Flags().GetString("placement") + if placement != "" && placement != "Spread" && placement != "Mirror" { + log.Fatalf(`❌ Must provide valid placement: Mirror | Spread | ""`) + } backingStores, _ := cmd.Flags().GetStringSlice("backingstores") if len(backingStores) == 0 { log.Fatalf(`❌ Must provide at least one backing store`) diff --git a/pkg/bundle/deploy.go b/pkg/bundle/deploy.go index fee3309f1..39af44940 100644 --- a/pkg/bundle/deploy.go +++ b/pkg/bundle/deploy.go @@ -1,8 +1,8 @@ package bundle -const Version = "2.0.5" +const Version = "2.0.6" -const Sha256_deploy_cluster_role_yaml = "f719ff8e0015a73d4e6ff322d2b30efa1cc89fcb3f856c06a5910785cb9e8dd8" +const Sha256_deploy_cluster_role_yaml = "b7002d09a74061e0d16e9414d60f97ed7f6a8fb3192699f957169e1170f2a669" const File_deploy_cluster_role_yaml = `apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -58,6 +58,18 @@ rules: - get - list - watch +- apiGroups: # from system:auth-delegator + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: # from system:auth-delegator + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create ` const Sha256_deploy_cluster_role_binding_yaml = "15c78355aefdceaf577bd96b4ae949ae424a3febdc8853be0917cf89a63941fc" @@ -94,7 +106,7 @@ spec: name: backing-store-secret-aws1 ` -const Sha256_deploy_crds_noobaa_v1alpha1_backingstore_crd_yaml = "57e474668ba94f1f0853a04d13f02fff9135caf1428d4963259e8590d8c35c0e" +const Sha256_deploy_crds_noobaa_v1alpha1_backingstore_crd_yaml = "253e43542cf6ca7cd76d16a77ccd7db6566428dd39766eca777b858af2c4fc61" const File_deploy_crds_noobaa_v1alpha1_backingstore_crd_yaml = `apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition @@ -276,8 +288,6 @@ spec: items: type: object type: array - required: - - phase type: object version: v1alpha1 versions: @@ -301,7 +311,7 @@ spec: - aws1 ` -const Sha256_deploy_crds_noobaa_v1alpha1_bucketclass_crd_yaml = "2362aac8610c170155687a2d11ba08bca30c7040a7e16c1228ccd94e6c3b7bec" +const Sha256_deploy_crds_noobaa_v1alpha1_bucketclass_crd_yaml = "29408a234dcde8822cc4e56938abed18fd597c737214494ddb0bcb2b7f4e57c1" const File_deploy_crds_noobaa_v1alpha1_bucketclass_crd_yaml = `apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition @@ -367,6 +377,9 @@ spec: placement: description: Placement specifies the type of placement for the tier If empty it should have a single backing store. + enum: + - Spread + - Mirror type: string type: object type: array @@ -414,8 +427,6 @@ spec: items: type: object type: array - required: - - phase type: object version: v1alpha1 versions: @@ -435,7 +446,7 @@ metadata: spec: {} ` -const Sha256_deploy_crds_noobaa_v1alpha1_noobaa_crd_yaml = "c81b0849d9ab3e16d61b34d85752907f7f3e3cebb322f28a67eaab0cc6a9d701" +const Sha256_deploy_crds_noobaa_v1alpha1_noobaa_crd_yaml = "cd83d27891e3dc29b118be1f524992636ef9e7208276fc1437b2c49d7de527df" const File_deploy_crds_noobaa_v1alpha1_noobaa_crd_yaml = `apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition @@ -492,6 +503,9 @@ spec: spec: description: Specification of the desired behavior of the noobaa system. properties: + affinity: + description: Affinity (optional) passed through to noobaa's pods + type: object coreResources: description: CoreResources (optional) overrides the default resource requirements for the server container @@ -545,6 +559,7 @@ spec: description: Most recently observed status of the noobaa system. properties: accounts: + description: Accounts reports accounts info for the admin account properties: admin: properties: @@ -606,6 +621,7 @@ spec: type: object type: array services: + description: Services reports addresses for the services properties: serviceMgmt: properties: @@ -713,13 +729,6 @@ spec: - serviceMgmt - serviceS3 type: object - required: - - observedGeneration - - phase - - actualImage - - accounts - - services - - readme type: object version: v1alpha1 versions: @@ -1028,7 +1037,7 @@ spec: name: s3-https ` -const Sha256_deploy_internal_statefulset_core_yaml = "97cb70ec5b66a8fec8225a441a84948368513e5db515320f8f3fcf8ed0bd01b1" +const Sha256_deploy_internal_statefulset_core_yaml = "effdb488acaf67b4cfbb2e1120f17a9d7283206f4a65f1cc1e687473d2bd96fd" const File_deploy_internal_statefulset_core_yaml = `apiVersion: apps/v1 kind: StatefulSet @@ -1145,6 +1154,10 @@ spec: value: "" - name: OAUTH_TOKEN_ENDPOINT value: "" + - name: NOOBAA_SERVICE_ACCOUNT + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName - name: container_dbg value: "" # any non-empty value will set the container to dbg mode - name: CONTAINER_CPU_REQUEST @@ -1798,7 +1811,7 @@ spec: sourceNamespace: marketplace ` -const Sha256_deploy_operator_yaml = "f69ccad7b56baceecde898de48bcd7b2fde6a2926c485529142641165ae20317" +const Sha256_deploy_operator_yaml = "da7953abeaba4a833719d5c197bc445049e6a3954f96cddfed7ee0c6a1f81dca" const File_deploy_operator_yaml = `apiVersion: apps/v1 kind: Deployment @@ -1818,7 +1831,7 @@ spec: serviceAccountName: noobaa containers: - name: noobaa-operator - image: noobaa/noobaa-operator:2.0.5 + image: noobaa/noobaa-operator:2.0.6 imagePullPolicy: IfNotPresent resources: limits: @@ -1837,7 +1850,7 @@ spec: fieldPath: metadata.namespace ` -const Sha256_deploy_role_yaml = "26c988090a0b9e2b50e2ffc225476534c3916d75ecb4d67e434a514eab52824c" +const Sha256_deploy_role_yaml = "dc9c707478bdb8e930a2a6697df9d97242c339ec9f99ca938b70d84b3393039f" const File_deploy_role_yaml = `apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -1866,14 +1879,7 @@ rules: - events - configmaps - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - serviceaccounts - resourceNames: - - noobaa verbs: - '*' - apiGroups: @@ -1966,11 +1972,13 @@ roleRef: name: noobaa ` -const Sha256_deploy_service_account_yaml = "51241cd291100562ccd8bec1625c3779e212a58a0a21d4042937a98c73245d66" +const Sha256_deploy_service_account_yaml = "7c68e5bd65c614787d7d4cdf80db8c14d9159ce8e940c5134d33d21dbe66893f" const File_deploy_service_account_yaml = `apiVersion: v1 kind: ServiceAccount metadata: name: noobaa + annotations: + serviceaccounts.openshift.io/oauth-redirectreference.noobaa-mgmt: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"noobaa-mgmt"}}' ` diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 3901a4b3c..62da65cd6 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -2,6 +2,7 @@ package cli import ( "flag" + "fmt" "math/rand" "os" "time" @@ -68,15 +69,34 @@ func Cmd() *cobra.Command { } // Root command - cmd := &cobra.Command{ + rootCmd := &cobra.Command{ Use: "noobaa", Short: logo, } - cmd.PersistentFlags().AddFlagSet(options.FlagSet) - cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) - - cmdOptions := options.Cmd() + rootCmd.PersistentFlags().AddFlagSet(options.FlagSet) + rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) + + optionsCmd := options.Cmd() + + completionCmd := &cobra.Command{ + Use: "completion", + Short: "Generates bash completion scripts", + Long: fmt.Sprintf(` +Load noobaa completion to bash: +(add to your ~/.bashrc and ~/.bash_profile to auto load) + +. <(%s completion) +`, rootCmd.Name()), + Run: func(cmd *cobra.Command, args []string) { + alias, _ := cmd.Flags().GetString("alias") + if alias != "" { + rootCmd.Use = alias + } + rootCmd.GenBashCompletion(os.Stdout) + }, + } + completionCmd.Flags().String("alias", "", "Custom alias name to generate the completion for") groups := templates.CommandGroups{{ Message: "Install:", @@ -104,15 +124,16 @@ func Cmd() *cobra.Command { }, }} - groups.Add(cmd) + groups.Add(rootCmd) - cmd.AddCommand( + rootCmd.AddCommand( version.Cmd(), - cmdOptions, + optionsCmd, + completionCmd, ) - templates.ActsAsRootCommand(cmd, []string{}, groups...) - templates.UseOptionsTemplates(cmdOptions) + templates.ActsAsRootCommand(rootCmd, []string{}, groups...) + templates.UseOptionsTemplates(optionsCmd) - return cmd + return rootCmd } diff --git a/pkg/controller/backingstore/backingstore_controller.go b/pkg/controller/backingstore/backingstore_controller.go index a7047fb5f..7d1eb664b 100644 --- a/pkg/controller/backingstore/backingstore_controller.go +++ b/pkg/controller/backingstore/backingstore_controller.go @@ -3,6 +3,7 @@ package backingstore import ( nbv1 "github.com/noobaa/noobaa-operator/v2/pkg/apis/noobaa/v1alpha1" "github.com/noobaa/noobaa-operator/v2/pkg/backingstore" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" diff --git a/pkg/controller/noobaa/noobaa_controller.go b/pkg/controller/noobaa/noobaa_controller.go index 1d22d1da9..b49ef31e8 100644 --- a/pkg/controller/noobaa/noobaa_controller.go +++ b/pkg/controller/noobaa/noobaa_controller.go @@ -2,10 +2,13 @@ package noobaa import ( nbv1 "github.com/noobaa/noobaa-operator/v2/pkg/apis/noobaa/v1alpha1" + "github.com/noobaa/noobaa-operator/v2/pkg/options" "github.com/noobaa/noobaa-operator/v2/pkg/system" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -56,5 +59,24 @@ func Add(mgr manager.Manager) error { return err } + // Watch for StorageClass changes to trigger reconcile and recreate it when deleted + err = c.Watch(&source.Kind{Type: &storagev1.StorageClass{}}, &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(func(mo handler.MapObject) []reconcile.Request { + sc, ok := mo.Object.(*storagev1.StorageClass) + if !ok || sc.Provisioner != options.ObjectBucketProvisionerName() { + return nil + } + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Name: options.SystemName, + Namespace: options.Namespace, + }, + }} + }), + }) + if err != nil { + return err + } + return nil } diff --git a/pkg/options/options.go b/pkg/options/options.go index 5daa7f918..8acb704cb 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -30,7 +30,7 @@ const ( // ContainerImageRepo is the repo of the default image url ContainerImageRepo = "noobaa-core" // ContainerImageTag is the tag of the default image url - ContainerImageTag = "5" + ContainerImageTag = "5.2.8" // ContainerImageConstraintSemver is the contraints of supported image versions ContainerImageConstraintSemver = ">=5, <6" // ContainerImageName is the default image name without the tag/version diff --git a/pkg/system/phase1_verifying.go b/pkg/system/phase1_verifying.go index 36f0b0507..f59a8bfbe 100644 --- a/pkg/system/phase1_verifying.go +++ b/pkg/system/phase1_verifying.go @@ -78,11 +78,11 @@ func (r *Reconciler) CheckSystemCR() error { log.Infof("Parsed version %q from image tag %q", version.String(), imageTag) if !ContainerImageConstraint.Check(version) { return util.NewPersistentError("InvalidImageVersion", - fmt.Sprintf(`Invalid image version %q not matching contraints %q`, + fmt.Sprintf(`Invalid image version %q not matching constraints %q`, imageRef, ContainerImageConstraint)) } } else { - log.Infof("Using custom image %q contraints %q", imageRef.String(), ContainerImageConstraint.String()) + log.Infof("Using custom image %q constraints %q", imageRef.String(), ContainerImageConstraint.String()) } } else { log.Infof("Using custom image name %q the default is %q", imageRef.String(), options.ContainerImageName) diff --git a/pkg/system/phase2_creating.go b/pkg/system/phase2_creating.go index 8ad9131d6..b783d87b4 100644 --- a/pkg/system/phase2_creating.go +++ b/pkg/system/phase2_creating.go @@ -27,6 +27,13 @@ func (r *Reconciler) ReconcilePhaseCreating() error { "noobaa operator started phase 2/4 - \"Creating\"", ) + // A failiure to discover OAuth endpoints should not fail the entire reconcile phase. + oAuthEndpoints, err := util.DiscoverOAuthEndpoints() + if err != nil { + r.Logger.Warnf("Discovery of OAuth endpoints failed, got: %v", err) + } + r.OAuthEndpoints = oAuthEndpoints + // the credentials that are created by cloud-credentials-operator sometimes take time // to be valid (requests sometimes returns InvalidAccessKeyId for 1-2 minutes) // creating the credential request as early as possible to try and avoid it @@ -34,6 +41,9 @@ func (r *Reconciler) ReconcilePhaseCreating() error { r.Logger.Errorf("failed to create CredentialsRequest. will retry in phase 4. error: %v", err) return err } + if err := r.ReconcileObject(r.ServiceAccount, r.SetDesiredServiceAccount); err != nil { + return err + } if err := r.ReconcileObject(r.SecretServer, nil); err != nil { return err } @@ -56,6 +66,12 @@ func (r *Reconciler) ReconcilePhaseCreating() error { return nil } +// SetDesiredServiceAccount updates the ServiceAccount as desired for reconciling +func (r *Reconciler) SetDesiredServiceAccount() { + r.ServiceAccount.Annotations["serviceaccounts.openshift.io/oauth-redirectreference.noobaa-mgmt"] = + `{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"` + r.RouteMgmt.Name + `"}}` +} + // SetDesiredServiceMgmt updates the ServiceMgmt as desired for reconciling func (r *Reconciler) SetDesiredServiceMgmt() { r.ServiceMgmt.Spec.Selector["noobaa-mgmt"] = r.Request.Name @@ -98,6 +114,16 @@ func (r *Reconciler) SetDesiredCoreApp() { // c.Env[j].Value = "1" // TODO recalculate case "AGENT_PROFILE": c.Env[j].Value = r.SetDesiredAgentProfile(c.Env[j].Value) + + case "OAUTH_AUTHORIZATION_ENDPOINT": + if r.OAuthEndpoints != nil { + c.Env[j].Value = r.OAuthEndpoints.AuthorizationEndpoint + } + + case "OAUTH_TOKEN_ENDPOINT": + if r.OAuthEndpoints != nil { + c.Env[j].Value = r.OAuthEndpoints.TokenEndpoint + } } } if r.NooBaa.Spec.CoreResources != nil { @@ -124,6 +150,9 @@ func (r *Reconciler) SetDesiredCoreApp() { if r.NooBaa.Spec.Tolerations != nil { podSpec.Tolerations = r.NooBaa.Spec.Tolerations } + if r.NooBaa.Spec.Affinity != nil { + podSpec.Affinity = r.NooBaa.Spec.Affinity + } if r.CoreApp.UID == "" { for i := range r.CoreApp.Spec.VolumeClaimTemplates { diff --git a/pkg/system/phase3_connecting.go b/pkg/system/phase3_connecting.go index 7b45ed21b..ed3f39eed 100644 --- a/pkg/system/phase3_connecting.go +++ b/pkg/system/phase3_connecting.go @@ -7,10 +7,10 @@ import ( nbv1 "github.com/noobaa/noobaa-operator/v2/pkg/apis/noobaa/v1alpha1" "github.com/noobaa/noobaa-operator/v2/pkg/nb" + routev1 "github.com/openshift/api/route/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" - routev1 "github.com/openshift/api/route/v1" ) // ReconcilePhaseConnecting runs the reconcile phase @@ -105,7 +105,7 @@ func (r *Reconciler) CheckServiceStatus(srv *corev1.Service, route *routev1.Rout ) status.InternalDNS = append( status.InternalDNS, - fmt.Sprintf("%s://%s.%s:%d", proto, srv.Name, srv.Namespace, servicePort.Port), + fmt.Sprintf("%s://%s.%s.svc:%d", proto, srv.Name, srv.Namespace, servicePort.Port), ) } diff --git a/pkg/system/reconciler.go b/pkg/system/reconciler.go index f3f63d41d..a3e3bb645 100644 --- a/pkg/system/reconciler.go +++ b/pkg/system/reconciler.go @@ -61,8 +61,10 @@ type Reconciler struct { NBClient nb.Client CoreVersion string OperatorVersion string + OAuthEndpoints *util.OAuth2Endpoints NooBaa *nbv1.NooBaa + ServiceAccount *corev1.ServiceAccount CoreApp *appsv1.StatefulSet ServiceMgmt *corev1.Service ServiceS3 *corev1.Service @@ -99,6 +101,7 @@ func NewReconciler( Ctx: context.TODO(), Logger: logrus.WithField("sys", req.Namespace+"/"+req.Name), NooBaa: util.KubeObject(bundle.File_deploy_crds_noobaa_v1alpha1_noobaa_cr_yaml).(*nbv1.NooBaa), + ServiceAccount: util.KubeObject(bundle.File_deploy_service_account_yaml).(*corev1.ServiceAccount), CoreApp: util.KubeObject(bundle.File_deploy_internal_statefulset_core_yaml).(*appsv1.StatefulSet), ServiceMgmt: util.KubeObject(bundle.File_deploy_internal_service_mgmt_yaml).(*corev1.Service), ServiceS3: util.KubeObject(bundle.File_deploy_internal_service_s3_yaml).(*corev1.Service), @@ -118,6 +121,7 @@ func NewReconciler( // Set Namespace r.NooBaa.Namespace = r.Request.Namespace + r.ServiceAccount.Namespace = r.Request.Namespace r.CoreApp.Namespace = r.Request.Namespace r.ServiceMgmt.Namespace = r.Request.Namespace r.ServiceS3.Namespace = r.Request.Namespace @@ -136,6 +140,7 @@ func NewReconciler( // Set Names r.NooBaa.Name = r.Request.Name + r.ServiceAccount.Name = r.Request.Name r.CoreApp.Name = r.Request.Name + "-core" r.ServiceMgmt.Name = r.Request.Name + "-mgmt" r.ServiceS3.Name = "s3" @@ -197,6 +202,13 @@ func (r *Reconciler) Reconcile() (reconcile.Result, error) { util.KubeCheck(r.NooBaa) + if r.NooBaa.Status.Accounts == nil { + r.NooBaa.Status.Accounts = &nbv1.AccountsStatus{} + } + if r.NooBaa.Status.Services == nil { + r.NooBaa.Status.Services = &nbv1.ServicesStatus{} + } + if r.NooBaa.UID == "" { log.Infof("NooBaa not found or already deleted. Skip reconcile.") return res, nil diff --git a/pkg/system/system.go b/pkg/system/system.go index 1a7de987c..af23cc45e 100644 --- a/pkg/system/system.go +++ b/pkg/system/system.go @@ -92,9 +92,10 @@ func CmdStatus() *cobra.Command { // CmdReconcile returns a CLI command func CmdReconcile() *cobra.Command { cmd := &cobra.Command{ - Use: "reconcile", - Short: "Runs a reconcile attempt like noobaa-operator", - Run: RunReconcile, + Hidden: true, + Use: "reconcile", + Short: "Runs a reconcile attempt like noobaa-operator", + Run: RunReconcile, } return cmd } diff --git a/pkg/util/util.go b/pkg/util/util.go index fd0cd3a16..720f0c60e 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -3,11 +3,16 @@ package util import ( "context" "crypto/rand" + "crypto/tls" "encoding/base64" "encoding/hex" + "encoding/json" "fmt" + "io/ioutil" + "net/http" "strings" "time" + "unicode" "golang.org/x/crypto/ssh/terminal" "k8s.io/apimachinery/pkg/util/wait" @@ -15,6 +20,7 @@ import ( monitoringv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1" obv1 "github.com/kube-object-storage/lib-bucket-provisioner/pkg/apis/objectbucket.io/v1alpha1" nbapis "github.com/noobaa/noobaa-operator/v2/pkg/apis" + routev1 "github.com/openshift/api/route/v1" cloudcredsv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1" operv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" @@ -34,9 +40,16 @@ import ( "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" - routev1 "github.com/openshift/api/route/v1" ) +const oAuthWellKnownEndpoint = "https://openshift.default.svc/.well-known/oauth-authorization-server" + +// Holds OAuth2 endpoints information. +type OAuth2Endpoints struct { + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` +} + var ( ctx = context.TODO() log = logrus.WithContext(ctx) @@ -721,3 +734,42 @@ func PrintThisNoteWhenFinishedApplyingAndStartWaitLoop() { log.Printf(" - From now on, it only loops and reads the status, to monitor the operator work.") log.Printf(" - You may Ctrl-C at any time to stop the loop and watch it manually.") } + +func DiscoverOAuthEndpoints() (*OAuth2Endpoints, error) { + client := http.Client{ + Timeout: 120 * time.Second, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + res, err := client.Get(oAuthWellKnownEndpoint) + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + endpoints := OAuth2Endpoints{} + err = json.Unmarshal(body, &endpoints) + if err != nil { + return nil, err + } + + return &endpoints, nil +} + +// IsStringGraphicCharsOnly returns true only if all the chars are graphic +func IsStringGraphicCharsOnly(s string) bool { + for _, c := range s { + if !unicode.IsGraphic(c) { + return false + } + } + return true +} + +// WriteYamlFile writes a yaml file from the given objects diff --git a/version/version.go b/version/version.go index a63159413..66436e762 100644 --- a/version/version.go +++ b/version/version.go @@ -2,5 +2,5 @@ package version const ( // Version is the noobaa-operator version (semver) - Version = "2.0.5" + Version = "2.0.6" )