diff --git a/api/v1/configurationpolicy_types.go b/api/v1/configurationpolicy_types.go index 323743dd..ada14940 100644 --- a/api/v1/configurationpolicy_types.go +++ b/api/v1/configurationpolicy_types.go @@ -6,11 +6,13 @@ package v1 import ( "errors" "fmt" + "strings" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" ) // A custom type is required since there is no way to have a kubebuilder marker @@ -40,6 +42,14 @@ const ( Inform RemediationAction = "Inform" ) +func (ra RemediationAction) IsInform() bool { + return strings.EqualFold(string(ra), string(Inform)) +} + +func (ra RemediationAction) IsEnforce() bool { + return strings.EqualFold(string(ra), string(Enforce)) +} + // ComplianceState shows the state of enforcement type ComplianceState string @@ -277,6 +287,18 @@ const ( MustOnlyHave ComplianceType = "Mustonlyhave" ) +func (c ComplianceType) IsMustHave() bool { + return strings.EqualFold(string(c), string(MustHave)) +} + +func (c ComplianceType) IsMustOnlyHave() bool { + return strings.EqualFold(string(c), string(MustOnlyHave)) +} + +func (c ComplianceType) IsMustNotHave() bool { + return strings.EqualFold(string(c), string(MustNotHave)) +} + // MetadataComplianceType describes how to check compliance for the labels/annotations of a given object // +kubebuilder:validation:Enum=MustHave;Musthave;musthave;MustOnlyHave;Mustonlyhave;mustonlyhave type MetadataComplianceType string @@ -303,6 +325,22 @@ type ObjectResource struct { Metadata ObjectMetadata `json:"metadata,omitempty"` } +func ObjectResourceFromObj(obj client.Object) ObjectResource { + name := obj.GetName() + if name == "" { + name = "*" + } + + return ObjectResource{ + Kind: obj.GetObjectKind().GroupVersionKind().Kind, + APIVersion: obj.GetObjectKind().GroupVersionKind().GroupVersion().String(), + Metadata: ObjectMetadata{ + Name: name, + Namespace: obj.GetNamespace(), + }, + } +} + // ObjectMetadata contains the resource metadata for an object being processed by the policy type ObjectMetadata struct { // Name of the referent. More info: diff --git a/api/v1beta1/operatorpolicy_types.go b/api/v1beta1/operatorpolicy_types.go index a5ca27f6..262ccde1 100644 --- a/api/v1beta1/operatorpolicy_types.go +++ b/api/v1beta1/operatorpolicy_types.go @@ -4,8 +4,8 @@ package v1beta1 import ( - operatorv1 "github.com/operator-framework/api/pkg/operators/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" policyv1 "open-cluster-management.io/config-policy-controller/api/v1" ) @@ -36,39 +36,6 @@ const ( DeleteIfUnused RemovalAction = "DeleteIfUnused" ) -type TargetNsOrSelector struct { - // 'namespaces' and 'selector' both define an array/set of target namespaces that - // should be affected on the cluster. Only one of 'namespaces' or 'selector' - // should be specified, and if both are set then 'selector' will be omitted. - Namespace []string `json:"namespaces,omitempty"` - // 'namespaces' and 'selector' both define an array/set of target namespaces that - // should be affected on the cluster. Only one of 'namespaces' or 'selector' - // should be specified, and if both are set then 'selector' will be omitted. - Selector *metav1.LabelSelector `json:"selector,omitempty"` -} - -// OperatorGroup specifies an OLM OperatorGroup. More info: -// https://olm.operatorframework.io/docs/concepts/crds/operatorgroup/ -type OperatorGroup struct { - // Name of the referent - Name string `json:"name,omitempty"` - // Namespace of the referent - Namespace string `json:"namespace,omitempty"` - // Target namespaces of the referent - Target TargetNsOrSelector `json:"target,omitempty"` - // Name of the OLM ServiceAccount that defines permissions for member operators - // +optional - ServiceAccountName string `json:"serviceAccountName,omitempty"` -} - -// SubscriptionSpec extends an OLM subscription with a namespace field. More info: -// https://olm.operatorframework.io/docs/concepts/crds/subscription/ -type SubscriptionSpec struct { - operatorv1.SubscriptionSpec `json:",inline"` - // Namespace of the referent - Namespace string `json:"namespace,omitempty"` -} - // RemovalBehavior defines resource behavior when policy is removed type RemovalBehavior struct { // Kind OperatorGroup @@ -97,20 +64,31 @@ type StatusConfig struct { type OperatorPolicySpec struct { Severity policyv1.Severity `json:"severity,omitempty"` // low, medium, high RemediationAction policyv1.RemediationAction `json:"remediationAction,omitempty"` // inform, enforce - ComplianceType policyv1.ComplianceType `json:"complianceType"` // Compliant, NonCompliant - // OperatorGroup requires at least 1 of target namespaces, or label selectors - // to be set to scope member operators' namespaced permissions. If both are provided, - // only the target namespace will be used and the label selector will be omitted. + ComplianceType policyv1.ComplianceType `json:"complianceType"` // musthave + + // Include the name, namespace, and any `spec` fields for the OperatorGroup. + // For more info, see `kubectl explain operatorgroup.spec` or + // https://olm.operatorframework.io/docs/concepts/crds/operatorgroup/ + // +kubebuilder:pruning:PreserveUnknownFields // +optional - OperatorGroup *OperatorGroup `json:"operatorGroup,omitempty"` - // Subscription defines an Application that can be installed + OperatorGroup *runtime.RawExtension `json:"operatorGroup,omitempty"` + + // Include the namespace, and any `spec` fields for the Subscription. + // For more info, see `kubectl explain subscription.spec` or + // https://olm.operatorframework.io/docs/concepts/crds/subscription/ // +kubebuilder:validation:Required - Subscription SubscriptionSpec `json:"subscription"` + // +kubebuilder:pruning:PreserveUnknownFields + Subscription runtime.RawExtension `json:"subscription"` + // Versions is a list of nonempty strings that specifies which installed versions are compliant when // in 'inform' mode, and which installPlans are approved when in 'enforce' mode - Versions []policyv1.NonEmptyString `json:"versions,omitempty"` - RemovalBehavior RemovalBehavior `json:"removalBehavior,omitempty"` - StatusConfig StatusConfig `json:"statusConfig,omitempty"` + Versions []policyv1.NonEmptyString `json:"versions,omitempty"` + + // FUTURE + //nolint:dupword + // RemovalBehavior RemovalBehavior `json:"removalBehavior,omitempty"` + //nolint:dupword + // StatusConfig StatusConfig `json:"statusConfig,omitempty"` } // OperatorPolicyStatus defines the observed state of OperatorPolicy @@ -118,9 +96,34 @@ type OperatorPolicyStatus struct { // Most recent compliance state of the policy ComplianceState policyv1.ComplianceState `json:"compliant,omitempty"` // Historic details on the condition of the policy - Condition []metav1.Condition `json:"conditions,omitempty"` + Conditions []metav1.Condition `json:"conditions,omitempty"` // List of resources processed by the policy - RelatedObject policyv1.RelatedObject `json:"relatedObject"` + RelatedObjects []policyv1.RelatedObject `json:"relatedObjects"` +} + +func (status OperatorPolicyStatus) RelatedObjsOfKind(kind string) map[int]policyv1.RelatedObject { + objs := make(map[int]policyv1.RelatedObject) + + for i, related := range status.RelatedObjects { + if related.Object.Kind == kind { + objs[i] = related + } + } + + return objs +} + +// Searches the conditions of the policy, and returns the index and condition matching the +// given condition Type. It will return -1 as the index if no condition of the specified +// Type is found. +func (status OperatorPolicyStatus) GetCondition(condType string) (int, metav1.Condition) { + for i, cond := range status.Conditions { + if cond.Type == condType { + return i, cond + } + } + + return -1, metav1.Condition{} } //+kubebuilder:object:root=true diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index c98dac3b..87e41e02 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -9,27 +9,11 @@ package v1beta1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - apiv1 "open-cluster-management.io/config-policy-controller/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "open-cluster-management.io/config-policy-controller/api/v1" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperatorGroup) DeepCopyInto(out *OperatorGroup) { - *out = *in - in.Target.DeepCopyInto(&out.Target) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorGroup. -func (in *OperatorGroup) DeepCopy() *OperatorGroup { - if in == nil { - return nil - } - out := new(OperatorGroup) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorPolicy) DeepCopyInto(out *OperatorPolicy) { *out = *in @@ -94,17 +78,15 @@ func (in *OperatorPolicySpec) DeepCopyInto(out *OperatorPolicySpec) { *out = *in if in.OperatorGroup != nil { in, out := &in.OperatorGroup, &out.OperatorGroup - *out = new(OperatorGroup) + *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } in.Subscription.DeepCopyInto(&out.Subscription) if in.Versions != nil { in, out := &in.Versions, &out.Versions - *out = make([]apiv1.NonEmptyString, len(*in)) + *out = make([]v1.NonEmptyString, len(*in)) copy(*out, *in) } - out.RemovalBehavior = in.RemovalBehavior - out.StatusConfig = in.StatusConfig } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorPolicySpec. @@ -120,14 +102,20 @@ func (in *OperatorPolicySpec) DeepCopy() *OperatorPolicySpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorPolicyStatus) DeepCopyInto(out *OperatorPolicyStatus) { *out = *in - if in.Condition != nil { - in, out := &in.Condition, &out.Condition - *out = make([]v1.Condition, len(*in)) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RelatedObjects != nil { + in, out := &in.RelatedObjects, &out.RelatedObjects + *out = make([]v1.RelatedObject, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.RelatedObject.DeepCopyInto(&out.RelatedObject) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorPolicyStatus. @@ -169,44 +157,3 @@ func (in *StatusConfig) DeepCopy() *StatusConfig { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionSpec) DeepCopyInto(out *SubscriptionSpec) { - *out = *in - in.SubscriptionSpec.DeepCopyInto(&out.SubscriptionSpec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionSpec. -func (in *SubscriptionSpec) DeepCopy() *SubscriptionSpec { - if in == nil { - return nil - } - out := new(SubscriptionSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TargetNsOrSelector) DeepCopyInto(out *TargetNsOrSelector) { - *out = *in - if in.Namespace != nil { - in, out := &in.Namespace, &out.Namespace - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetNsOrSelector. -func (in *TargetNsOrSelector) DeepCopy() *TargetNsOrSelector { - if in == nil { - return nil - } - out := new(TargetNsOrSelector) - in.DeepCopyInto(out) - return out -} diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index 525d24c5..57e4626f 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -68,7 +68,7 @@ var ( plcFmtStr = "policy: %s" ) -var ( +const ( reasonWantFoundExists = "Resource found as expected" reasonWantFoundCreated = "K8s creation success" reasonUpdateSuccess = "K8s update success" @@ -547,7 +547,7 @@ func (r *ConfigurationPolicyReconciler) cleanUpChildObjects(plc policyv1.Configu ) []string { deletionFailures := []string{} - if !strings.EqualFold(string(plc.Spec.RemediationAction), "enforce") { + if !plc.Spec.RemediationAction.IsEnforce() { return deletionFailures } @@ -1535,7 +1535,7 @@ func (r *ConfigurationPolicyReconciler) handleObjects( ) // we do not support enforce on unnamed templates - if !strings.EqualFold(string(remediation), "inform") { + if !remediation.IsInform() { log.Info( "The object template does not specify a name. Setting the remediation action to inform.", "oldRemediationAction", remediation, @@ -1555,7 +1555,7 @@ func (r *ConfigurationPolicyReconciler) handleObjects( } } - objShouldExist := !strings.EqualFold(string(objectT.ComplianceType), string(policyv1.MustNotHave)) + objShouldExist := !objectT.ComplianceType.IsMustNotHave() shouldAddCondensedRelatedObj := false @@ -1707,7 +1707,7 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( result.events = append(result.events, objectTmplEvalEvent{false, reasonWantFoundDNE, ""}) // it is a musthave and it does not exist, so it must be created - if strings.EqualFold(string(remediation), string(policyv1.Enforce)) { + if remediation.IsEnforce() { var uid string completed, reason, msg, uid, err := r.enforceByCreatingOrDeleting(obj) @@ -1741,7 +1741,7 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( if exists && !obj.shouldExist { // it is a mustnothave but it exist, so it must be deleted - if strings.EqualFold(string(remediation), string(policyv1.Enforce)) { + if remediation.IsEnforce() { completed, reason, msg, _, err := r.enforceByCreatingOrDeleting(obj) if err != nil { objLog.Error(err, "Could not handle existing mustnothave object") @@ -1798,7 +1798,7 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( result.events = append(result.events, objectTmplEvalEvent{false, resultReason, resultMsg}) } else { // it is a must have and it does exist, so it is compliant - if strings.EqualFold(string(remediation), string(policyv1.Enforce)) { + if remediation.IsEnforce() { if updatedObj { result.events = append(result.events, objectTmplEvalEvent{true, reasonUpdateSuccess, ""}) } else { @@ -2586,72 +2586,28 @@ func (r *ConfigurationPolicyReconciler) checkAndUpdateResource( res = r.TargetK8sDynamicClient.Resource(obj.gvr) } - updateSucceeded = false // Use a copy since some values can be directly assigned to mergedObj in handleSingleKey. existingObjectCopy := obj.existingObj.DeepCopy() removeFieldsForComparison(existingObjectCopy) - var statusMismatch bool - - for key := range obj.desiredObj.Object { - isStatus := key == "status" - - // use metadatacompliancetype to evaluate metadata if it is set - keyComplianceType := complianceType - if key == "metadata" && mdComplianceType != "" { - keyComplianceType = mdComplianceType - } - - // check key for mismatch - errorMsg, keyUpdateNeeded, mergedObj, skipped := handleSingleKey( - key, obj.desiredObj, existingObjectCopy, keyComplianceType, !r.DryRunSupported, - ) - if errorMsg != "" { - log.Info(errorMsg) - - return true, errorMsg, true, false - } - - if mergedObj == nil && skipped { - continue - } - - // only look at labels and annotations for metadata - configurationPolicies do not update other metadata fields - if key == "metadata" { - // if it's not the right type, the map will be empty - mdMap, _ := mergedObj.(map[string]interface{}) + throwSpecViolation, message, updateNeeded, statusMismatch := handleKeys( + obj.desiredObj, obj.existingObj, existingObjectCopy, complianceType, mdComplianceType, !r.DryRunSupported, + ) + if message != "" { + return true, message, true, false + } - // if either isn't found, they'll just be empty - mergedAnnotations, _, _ := unstructured.NestedStringMap(mdMap, "annotations") - mergedLabels, _, _ := unstructured.NestedStringMap(mdMap, "labels") + if updateNeeded { + mismatchLog := "Detected value mismatch" - obj.existingObj.SetAnnotations(mergedAnnotations) - obj.existingObj.SetLabels(mergedLabels) - } else { - obj.existingObj.UnstructuredContent()[key] = mergedObj + // Add a configuration breadcrumb for users that might be looking in the logs for a diff + if objectT.RecordDiff != policyv1.RecordDiffLog { + mismatchLog += " (Diff disabled. To log the diff, " + + "set 'spec.object-tempates[].recordDiff' to 'Log' for this object-template.)" } - if keyUpdateNeeded { - if isStatus { - throwSpecViolation = true - statusMismatch = true - - log.Info("Ignoring an update to the object status", "key", key) - } else { - updateNeeded = true + log.Info(mismatchLog) - mismatchLog := "Detected value mismatch for object key: " + key - // Add a configuration breadcrumb for users that might be looking in the logs for a diff - if objectT.RecordDiff != policyv1.RecordDiffLog { - mismatchLog += " (Diff disabled. To log the diff, " + - "set 'spec.object-tempates[].recordDiff' to 'Log' for this object-template.)" - } - log.Info(mismatchLog) - } - } - } - - if updateNeeded { // FieldValidation is supported in k8s 1.25 as beta release // so if the version is below 1.25, we need to use client side validation to validate the object if semver.Compare(r.serverVersion, "v1.25.0") < 0 { @@ -2739,7 +2695,7 @@ func (r *ConfigurationPolicyReconciler) checkAndUpdateResource( } // The object would have been updated, so if it's inform, return as noncompliant. - if strings.EqualFold(string(remediation), string(policyv1.Inform)) { + if remediation.IsInform() { r.setEvaluatedObject(obj.policy, obj.existingObj, false) return true, "", false, false @@ -2783,6 +2739,68 @@ func (r *ConfigurationPolicyReconciler) checkAndUpdateResource( return throwSpecViolation, "", updateNeeded, updateSucceeded } +// handleKeys goes through all of the fields in the desired object and checks if the existing object +// matches. When a field is a map or slice, the value in the existing object will be updated with +// the result of merging its current value with the desired value. +func handleKeys( + desiredObj unstructured.Unstructured, + existingObj *unstructured.Unstructured, + existingObjectCopy *unstructured.Unstructured, + compType string, + mdCompType string, + zeroValueEqualsNil bool, +) (throwSpecViolation bool, message string, updateNeeded bool, statusMismatch bool) { + for key := range desiredObj.Object { + isStatus := key == "status" + + // use metadatacompliancetype to evaluate metadata if it is set + keyComplianceType := compType + if key == "metadata" && mdCompType != "" { + keyComplianceType = mdCompType + } + + // check key for mismatch + errorMsg, keyUpdateNeeded, mergedObj, skipped := handleSingleKey( + key, desiredObj, existingObjectCopy, keyComplianceType, zeroValueEqualsNil, + ) + if errorMsg != "" { + log.Info(errorMsg) + + return true, errorMsg, true, statusMismatch + } + + if mergedObj == nil && skipped { + continue + } + + // only look at labels and annotations for metadata - configurationPolicies do not update other metadata fields + if key == "metadata" { + // if it's not the right type, the map will be empty + mdMap, _ := mergedObj.(map[string]interface{}) + + // if either isn't found, they'll just be empty + mergedAnnotations, _, _ := unstructured.NestedStringMap(mdMap, "annotations") + mergedLabels, _, _ := unstructured.NestedStringMap(mdMap, "labels") + + existingObj.SetAnnotations(mergedAnnotations) + existingObj.SetLabels(mergedLabels) + } else { + existingObj.UnstructuredContent()[key] = mergedObj + } + + if keyUpdateNeeded { + if isStatus { + throwSpecViolation = true + statusMismatch = true + } else { + updateNeeded = true + } + } + } + + return +} + func removeFieldsForComparison(obj *unstructured.Unstructured) { unstructured.RemoveNestedField(obj.Object, "metadata", "managedFields") unstructured.RemoveNestedField( diff --git a/controllers/operatorpolicy_controller.go b/controllers/operatorpolicy_controller.go index d708ee94..b0e6fe74 100644 --- a/controllers/operatorpolicy_controller.go +++ b/controllers/operatorpolicy_controller.go @@ -5,16 +5,21 @@ package controllers import ( "context" + "encoding/json" + "errors" "fmt" - "strings" + "reflect" operatorv1 "github.com/operator-framework/api/pkg/operators/v1" operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" depclient "github.com/stolostron/kubernetes-dependency-watches/client" - "k8s.io/apimachinery/pkg/api/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -23,13 +28,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - policyv1 "open-cluster-management.io/config-policy-controller/api/v1" policyv1beta1 "open-cluster-management.io/config-policy-controller/api/v1beta1" ) const ( OperatorControllerName string = "operator-policy-controller" - defaultOGSuffix string = "-default-og" ) var ( @@ -37,12 +40,11 @@ var ( operatorGroupGVK = schema.GroupVersionKind{Group: "operators.coreos.com", Version: "v1", Kind: "OperatorGroup"} ) -var OpLog = ctrl.Log.WithName(OperatorControllerName) - // OperatorPolicyReconciler reconciles a OperatorPolicy object type OperatorPolicyReconciler struct { client.Client DynamicWatcher depclient.DynamicWatcher + InstanceName string } // SetupWithManager sets up the controller with the Manager and will reconcile when the dynamic watcher @@ -76,21 +78,15 @@ var _ reconcile.Reconciler = &OperatorPolicyReconciler{} // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.4/pkg/reconcile func (r *OperatorPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + OpLog := ctrl.LoggerFrom(ctx) policy := &policyv1beta1.OperatorPolicy{} - - watcher := depclient.ObjectIdentifier{ - Group: operatorv1.GroupVersion.Group, - Version: operatorv1.GroupVersion.Version, - Kind: "OperatorPolicy", - Namespace: req.Namespace, - Name: req.Name, - } + watcher := opPolIdentifier(req.Namespace, req.Name) // Get the applied OperatorPolicy err := r.Get(ctx, req.NamespacedName, policy) if err != nil { - if errors.IsNotFound(err) { - OpLog.Info("Operator policy could not be found", "name", req.Name, "namespace", req.Namespace) + if k8serrors.IsNotFound(err) { + OpLog.Info("Operator policy could not be found") err = r.DynamicWatcher.RemoveWatcher(watcher) if err != nil { @@ -108,8 +104,7 @@ func (r *OperatorPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Start query batch for caching and watching related objects err = r.DynamicWatcher.StartQueryBatch(watcher) if err != nil { - OpLog.Error(err, "Could not start query batch for the watcher", "watcher", watcher.Name, - "watcherKind", watcher.Kind) + OpLog.Error(err, "Could not start query batch for the watcher") return reconcile.Result{}, err } @@ -117,158 +112,341 @@ func (r *OperatorPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reque defer func() { err := r.DynamicWatcher.EndQueryBatch(watcher) if err != nil { - OpLog.Error(err, "Could not end query batch for the watcher", "watcher", watcher.Name, - "watcherKind", watcher.Kind) + OpLog.Error(err, "Could not end query batch for the watcher") } }() // handle the policy - OpLog.Info("Reconciling OperatorPolicy", "policy", policy.Name) + OpLog.Info("Reconciling OperatorPolicy") - // Check if specified Subscription exists - cachedSubscription, err := r.DynamicWatcher.Get(watcher, subscriptionGVK, policy.Spec.Subscription.Namespace, - policy.Spec.Subscription.SubscriptionSpec.Package) - if err != nil { - OpLog.Error(err, "Could not get subscription", "kind", subscriptionGVK.Kind, - "name", policy.Spec.Subscription.SubscriptionSpec.Package, - "namespace", policy.Spec.Subscription.Namespace) + if err := r.handleOpGroup(ctx, policy); err != nil { + OpLog.Error(err, "Error handling OperatorGroup") return reconcile.Result{}, err } - subExists := cachedSubscription != nil - - // Check if any OperatorGroups exist in the namespace - ogNamespace := policy.Spec.Subscription.Namespace - if policy.Spec.OperatorGroup != nil { - ogNamespace = policy.Spec.OperatorGroup.Namespace - } - - cachedOperatorGroups, err := r.DynamicWatcher.List(watcher, operatorGroupGVK, ogNamespace, nil) - if err != nil { - OpLog.Error(err, "Could not list operator group", "kind", operatorGroupGVK.Kind, - "namespace", ogNamespace) + if err := r.handleSubscription(ctx, policy); err != nil { + OpLog.Error(err, "Error handling Subscription") return reconcile.Result{}, err } - ogExists := len(cachedOperatorGroups) != 0 + // FUTURE: more resource checks - // Exists indicates if a Subscription or Operatorgroup need to be created - exists := subExists && ogExists - shouldExist := strings.EqualFold(string(policy.Spec.ComplianceType), string(policyv1.MustHave)) + return reconcile.Result{}, nil +} - // Case 1: policy has just been applied and related resources have yet to be created - if !exists && shouldExist { - OpLog.Info("The object does not exist but should exist") +func (r *OperatorPolicyReconciler) handleOpGroup(ctx context.Context, policy *policyv1beta1.OperatorPolicy) error { + watcher := opPolIdentifier(policy.Namespace, policy.Name) - return r.createPolicyResources(ctx, policy, cachedSubscription, cachedOperatorGroups) + desiredOpGroup, err := buildOperatorGroup(policy) + if err != nil { + return fmt.Errorf("error building operator group: %w", err) } - // Case 2: Resources exist, but should not exist (i.e. mustnothave or deletion) - if exists && !shouldExist { - // Future implementation: clean up resources and delete watches if mustnothave, otherwise inform - OpLog.Info("The object exists but should not exist") + foundOpGroups, err := r.DynamicWatcher.List( + watcher, operatorGroupGVK, desiredOpGroup.Namespace, labels.Everything()) + if err != nil { + return fmt.Errorf("error listing OperatorGroups: %w", err) } - // Case 3: Resources do not exist, and should not exist - if !exists && !shouldExist { - // Future implementation: Possibly emit a success event - OpLog.Info("The object does not exist and is compliant with the mustnothave compliance type") - } + switch len(foundOpGroups) { + case 0: + // Missing OperatorGroup: report NonCompliance + err := r.updateStatus(ctx, policy, missingWantedCond("OperatorGroup"), missingWantedObj(desiredOpGroup)) + if err != nil { + return fmt.Errorf("error updating the status for a missing OperatorGroup: %w", err) + } - // Case 4: Resources exist, and should exist (i.e. update) - if exists && shouldExist { - // Future implementation: Verify the specs of the object matches the one on the cluster - OpLog.Info("The object already exists. Checking fields to verify matching specs") - } + if policy.Spec.RemediationAction.IsEnforce() { + err = r.Create(ctx, desiredOpGroup) + if err != nil { + return fmt.Errorf("error creating the OperatorGroup: %w", err) + } - return reconcile.Result{}, nil -} + desiredOpGroup.SetGroupVersionKind(operatorGroupGVK) // Create stripped this information -// createPolicyResources encapsulates the logic for creating resources specified within an operator policy. -// This should normally happen when the policy is initially applied to the cluster. Creates an OperatorGroup -// if specified, otherwise defaults to using an allnamespaces OperatorGroup. -func (r *OperatorPolicyReconciler) createPolicyResources( - ctx context.Context, - policy *policyv1beta1.OperatorPolicy, - cachedSubscription *unstructured.Unstructured, - cachedOGList []unstructured.Unstructured, -) (ctrl.Result, error) { - // Create og, then trigger reconcile - if len(cachedOGList) == 0 { - if strings.EqualFold(string(policy.Spec.RemediationAction), string(policyv1.Enforce)) { - ogSpec := buildOperatorGroup(policy) - - err := r.Create(ctx, ogSpec) + // Now the OperatorGroup should match, so report Compliance + err = r.updateStatus(ctx, policy, createdCond("OperatorGroup"), createdObj(desiredOpGroup)) if err != nil { - OpLog.Error(err, "Error while creating OperatorGroup") - r.setCompliance(ctx, policy, policyv1.NonCompliant) + return fmt.Errorf("error updating the status for a created OperatorGroup: %w", err) + } + } + case 1: + opGroup := foundOpGroups[0] + + // Check if what's on the cluster matches what the policy wants (whether it's specified or not) + + emptyNameMatch := desiredOpGroup.Name == "" && opGroup.GetGenerateName() == desiredOpGroup.GenerateName + + if !(opGroup.GetName() == desiredOpGroup.Name || emptyNameMatch) { + if policy.Spec.OperatorGroup == nil { + // The policy doesn't specify what the OperatorGroup should look like, but what is already + // there is not the default one the policy would create. + // FUTURE: check if the one operator group is compatible with the desired subscription. + // For an initial implementation, assume if an OperatorGroup already exists, then it's a good one. + err := r.updateStatus(ctx, policy, opGroupPreexistingCond, matchedObj(&opGroup)) + if err != nil { + return fmt.Errorf("error updating the status for a pre-existing OperatorGroup: %w", err) + } - return reconcile.Result{}, err + return nil } - // Created successfully, requeue result - r.setCompliance(ctx, policy, policyv1.NonCompliant) + // There is an OperatorGroup in the namespace that does not match the name of what is in the policy. + // Just creating a new one would cause the "TooManyOperatorGroups" failure. + // So, just report a NonCompliant status. + missing := missingWantedObj(desiredOpGroup) + badExisting := mismatchedObj(&opGroup) - return reconcile.Result{Requeue: true}, err - } else if policy.Spec.OperatorGroup != nil { - // If inform mode, keep going and return at the end without requeue. Before - // continuing, should check if required og spec is created to set compliance state + err := r.updateStatus(ctx, policy, mismatchCond("OperatorGroup"), missing, badExisting) + if err != nil { + return fmt.Errorf("error updating the status for an OperatorGroup with the wrong name: %w", err) + } - // Set to non compliant because operatorgroup does not exist on cluster, but - // should still go to check subscription - r.setCompliance(ctx, policy, policyv1.NonCompliant) + return nil } - } - // Create new subscription - if cachedSubscription == nil { - if strings.EqualFold(string(policy.Spec.RemediationAction), string(policyv1.Enforce)) { - subscriptionSpec := buildSubscription(policy) + // check whether the specs match + desiredUnstruct, err := runtime.DefaultUnstructuredConverter.ToUnstructured(desiredOpGroup) + if err != nil { + return fmt.Errorf("error converting desired OperatorGroup to an Unstructured: %w", err) + } - err := r.Create(ctx, subscriptionSpec) + merged := opGroup.DeepCopy() // Copy it so that the value in the cache is not changed + + updateNeeded, skipUpdate, err := r.mergeObjects( + ctx, desiredUnstruct, merged, string(policy.Spec.ComplianceType), + ) + if err != nil { + return fmt.Errorf("error checking if the OperatorGroup needs an update: %w", err) + } + + if !updateNeeded { + // Everything relevant matches! + err := r.updateStatus(ctx, policy, matchesCond("OperatorGroup"), matchedObj(&opGroup)) if err != nil { - OpLog.Error(err, "Could not handle missing musthave object") - r.setCompliance(ctx, policy, policyv1.NonCompliant) + return fmt.Errorf("error updating the status for an OperatorGroup that matches: %w", err) + } - return reconcile.Result{}, err + return nil + } + + // Specs don't match. + + if policy.Spec.OperatorGroup == nil { + // The policy doesn't specify what the OperatorGroup should look like, but what is already + // there is not the default one the policy would create. + // FUTURE: check if the one operator group is compatible with the desired subscription. + // For an initial implementation, assume if an OperatorGroup already exists, then it's a good one. + err := r.updateStatus(ctx, policy, opGroupPreexistingCond, matchedObj(&opGroup)) + if err != nil { + return fmt.Errorf("error updating the status for a pre-existing OperatorGroup: %w", err) } - // Future work: Check availability/status of all resources - // managed by the OperatorPolicy before setting compliance state - r.setCompliance(ctx, policy, policyv1.Compliant) + return nil + } - return reconcile.Result{}, nil + if policy.Spec.RemediationAction.IsEnforce() && skipUpdate { + err = r.updateStatus(ctx, policy, mismatchCondUnfixable("OperatorGroup"), mismatchedObj(&opGroup)) + if err != nil { + return fmt.Errorf("error updating status for an unenforceable mismatched OperatorGroup: %w", err) + } + + return nil + } + + // The names match, but the specs don't: report NonCompliance + err = r.updateStatus(ctx, policy, mismatchCond("OperatorGroup"), mismatchedObj(&opGroup)) + if err != nil { + return fmt.Errorf("error updating the status for an OperatorGroup that does not match: %w", err) + } + + if policy.Spec.RemediationAction.IsEnforce() { + desiredOpGroup.ResourceVersion = opGroup.GetResourceVersion() + + err := r.Update(ctx, merged) + if err != nil { + return fmt.Errorf("error updating the OperatorGroup: %w", err) + } + + desiredOpGroup.SetGroupVersionKind(operatorGroupGVK) // Update stripped this information + + // It was updated and should match now, so report Compliance + err = r.updateStatus(ctx, policy, updatedCond("OperatorGroup"), updatedObj(desiredOpGroup)) + if err != nil { + return fmt.Errorf("error updating the status after updating the OperatorGroup: %w", err) + } + } + default: + // This situation will always lead to a "TooManyOperatorGroups" failure on the CSV. + // Consider improving this in the future: perhaps this could suggest one of the OperatorGroups to keep. + err := r.updateStatus(ctx, policy, opGroupTooManyCond, opGroupTooManyObjs(foundOpGroups)...) + if err != nil { + return fmt.Errorf("error updating the status when there are multiple OperatorGroups: %w", err) } - // inform - r.setCompliance(ctx, policy, policyv1.NonCompliant) } - // Will only reach this if in inform mode - return reconcile.Result{}, nil + return nil } -// updatePolicyStatus updates the status of the operatorPolicy. -// In the future, a condition should be added as well, and this should generate events. -func (r *OperatorPolicyReconciler) updatePolicyStatus( - ctx context.Context, +// buildOperatorGroup bootstraps the OperatorGroup spec defined in the operator policy +// with the apiversion and kind in preparation for resource creation +func buildOperatorGroup( policy *policyv1beta1.OperatorPolicy, -) error { - updatedStatus := policy.Status +) (*operatorv1.OperatorGroup, error) { + operatorGroup := new(operatorv1.OperatorGroup) + + operatorGroup.Status.LastUpdated = &metav1.Time{} // without this, some conversions can panic + operatorGroup.SetGroupVersionKind(operatorGroupGVK) + + sub := make(map[string]interface{}) + + err := json.Unmarshal(policy.Spec.Subscription.Raw, &sub) + if err != nil { + return nil, fmt.Errorf("error unmarshalling subscription: %w", err) + } + + subNamespace, ok := sub["namespace"].(string) + if !ok { + return nil, fmt.Errorf("namespace is required in spec.subscription") + } + + if validationErrs := validation.IsDNS1123Label(subNamespace); len(validationErrs) != 0 { + return nil, fmt.Errorf("the namespace specified in spec.subscription is not a valid namespace identifier") + } + + // Create a default OperatorGroup if one wasn't specified in the policy + if policy.Spec.OperatorGroup == nil { + operatorGroup.ObjectMeta.SetNamespace(subNamespace) + operatorGroup.ObjectMeta.SetGenerateName(subNamespace + "-") // This matches what the console creates + operatorGroup.Spec.TargetNamespaces = []string{} + + return operatorGroup, nil + } + + opGroup := make(map[string]interface{}) + + err = json.Unmarshal(policy.Spec.OperatorGroup.Raw, &opGroup) + if err != nil { + return nil, fmt.Errorf("error unmarshalling operatorGroup: %w", err) + } + + // Fallback to the Subscription namespace if the OperatorGroup namespace is not specified in the policy. + ogNamespace := subNamespace - err := r.Get(ctx, types.NamespacedName{Namespace: policy.Namespace, Name: policy.Name}, policy) + if specifiedNS, ok := opGroup["namespace"].(string); ok || specifiedNS == "" { + ogNamespace = specifiedNS + } + + name, ok := opGroup["name"].(string) + if !ok { + return nil, fmt.Errorf("name is required in operatorGroup.spec") + } + + spec := new(operatorv1.OperatorGroupSpec) + + err = json.Unmarshal(policy.Spec.OperatorGroup.Raw, spec) if err != nil { - OpLog.Info(fmt.Sprintf("Failed to refresh policy; using previously fetched version: %s", err)) - } else { - policy.Status = updatedStatus + return nil, fmt.Errorf("error unmarshalling subscription: %w", err) } - err = r.Status().Update(ctx, policy) + operatorGroup.ObjectMeta.SetName(name) + operatorGroup.ObjectMeta.SetNamespace(ogNamespace) + operatorGroup.Spec = *spec + + return operatorGroup, nil +} + +func (r *OperatorPolicyReconciler) handleSubscription(ctx context.Context, policy *policyv1beta1.OperatorPolicy) error { + watcher := opPolIdentifier(policy.Namespace, policy.Name) + + desiredSub, err := buildSubscription(policy) if err != nil { - OpLog.Info(fmt.Sprintf("Failed to update policy status: %s", err)) + return fmt.Errorf("error building subscription: %w", err) + } - return err + foundSub, err := r.DynamicWatcher.Get(watcher, subscriptionGVK, desiredSub.Namespace, desiredSub.Name) + if err != nil { + return fmt.Errorf("error getting the Subscription: %w", err) + } + + if foundSub == nil { + // Missing Subscription: report NonCompliance + err := r.updateStatus(ctx, policy, missingWantedCond("Subscription"), missingWantedObj(desiredSub)) + if err != nil { + return fmt.Errorf("error updating status for a missing Subscription: %w", err) + } + + if policy.Spec.RemediationAction.IsEnforce() { + err := r.Create(ctx, desiredSub) + if err != nil { + return fmt.Errorf("error creating the Subscription: %w", err) + } + + desiredSub.SetGroupVersionKind(subscriptionGVK) // Create stripped this information + + // Now it should match, so report Compliance + err = r.updateStatus(ctx, policy, createdCond("Subscription"), createdObj(desiredSub)) + if err != nil { + return fmt.Errorf("error updating the status for a created Subscription: %w", err) + } + } + + return nil + } + + // Subscription found; check if specs match + desiredUnstruct, err := runtime.DefaultUnstructuredConverter.ToUnstructured(desiredSub) + if err != nil { + return fmt.Errorf("error converting desired Subscription to an Unstructured: %w", err) + } + + merged := foundSub.DeepCopy() // Copy it so that the value in the cache is not changed + + updateNeeded, skipUpdate, err := r.mergeObjects(ctx, desiredUnstruct, merged, string(policy.Spec.ComplianceType)) + if err != nil { + return fmt.Errorf("error checking if the Subscription needs an update: %w", err) + } + + if !updateNeeded { + // FUTURE: Check more details about the *status* of the Subscription + // For now, just mark it as compliant + err := r.updateStatus(ctx, policy, matchesCond("Subscription"), matchedObj(foundSub)) + if err != nil { + return fmt.Errorf("error updating the status for an OperatorGroup that matches: %w", err) + } + + return nil + } + + // Specs don't match. + if policy.Spec.RemediationAction.IsEnforce() && skipUpdate { + err = r.updateStatus(ctx, policy, mismatchCondUnfixable("Subscription"), mismatchedObj(foundSub)) + if err != nil { + return fmt.Errorf("error updating status for a mismatched Subscription that can't be enforced: %w", err) + } + + return nil + } + + err = r.updateStatus(ctx, policy, mismatchCond("Subscription"), mismatchedObj(foundSub)) + if err != nil { + return fmt.Errorf("error updating status for a mismatched Subscription: %w", err) + } + + if policy.Spec.RemediationAction.IsEnforce() { + err := r.Update(ctx, merged) + if err != nil { + return fmt.Errorf("error updating the Subscription: %w", err) + } + + desiredSub.SetGroupVersionKind(subscriptionGVK) // Update stripped this information + + err = r.updateStatus(ctx, policy, updatedCond("Subscription"), updatedObj(desiredSub)) + if err != nil { + return fmt.Errorf("error updating status after updating the Subscription: %w", err) + } } return nil @@ -278,57 +456,88 @@ func (r *OperatorPolicyReconciler) updatePolicyStatus( // with the apiversion and kind in preparation for resource creation func buildSubscription( policy *policyv1beta1.OperatorPolicy, -) *operatorv1alpha1.Subscription { +) (*operatorv1alpha1.Subscription, error) { subscription := new(operatorv1alpha1.Subscription) - subscription.SetGroupVersionKind(subscriptionGVK) - subscription.ObjectMeta.Name = policy.Spec.Subscription.Package - subscription.ObjectMeta.Namespace = policy.Spec.Subscription.Namespace - subscription.Spec = policy.Spec.Subscription.SubscriptionSpec.DeepCopy() + sub := make(map[string]interface{}) - OpLog.Info("Creating the subscription", "kind", subscription.Kind, - "namespace", subscription.Namespace) + err := json.Unmarshal(policy.Spec.Subscription.Raw, &sub) + if err != nil { + return nil, fmt.Errorf("error unmarshalling subscription: %w", err) + } - return subscription -} + ns, ok := sub["namespace"].(string) + if !ok { + return nil, fmt.Errorf("namespace is required in spec.subscription") + } -// Sets the compliance of the policy -func (r *OperatorPolicyReconciler) setCompliance( - ctx context.Context, - policy *policyv1beta1.OperatorPolicy, - compliance policyv1.ComplianceState, -) { - policy.Status.ComplianceState = compliance + spec := new(operatorv1alpha1.SubscriptionSpec) - err := r.updatePolicyStatus(ctx, policy) + err = json.Unmarshal(policy.Spec.Subscription.Raw, spec) if err != nil { - OpLog.Error(err, "error while updating policy status") + return nil, fmt.Errorf("error unmarshalling subscription: %w", err) } + + subscription.SetGroupVersionKind(subscriptionGVK) + subscription.ObjectMeta.Name = spec.Package + subscription.ObjectMeta.Namespace = ns + subscription.Spec = spec + + return subscription, nil } -// buildOperatorGroup bootstraps the OperatorGroup spec defined in the operator policy -// with the apiversion and kind in preparation for resource creation -func buildOperatorGroup( - policy *policyv1beta1.OperatorPolicy, -) *operatorv1.OperatorGroup { - operatorGroup := new(operatorv1.OperatorGroup) +func opPolIdentifier(namespace, name string) depclient.ObjectIdentifier { + return depclient.ObjectIdentifier{ + Group: policyv1beta1.GroupVersion.Group, + Version: policyv1beta1.GroupVersion.Version, + Kind: "OperatorPolicy", + Namespace: namespace, + Name: name, + } +} - // Create a default OperatorGroup if one wasn't specified in the policy - // Future work: Prevent creating multiple OperatorGroups in the same ns - if policy.Spec.OperatorGroup == nil { - operatorGroup.SetGroupVersionKind(operatorGroupGVK) - operatorGroup.ObjectMeta.SetName(policy.Spec.Subscription.Package + defaultOGSuffix) - operatorGroup.ObjectMeta.SetNamespace(policy.Spec.Subscription.Namespace) - operatorGroup.Spec.TargetNamespaces = []string{} - } else { - operatorGroup.SetGroupVersionKind(operatorGroupGVK) - operatorGroup.ObjectMeta.SetName(policy.Spec.OperatorGroup.Name) - operatorGroup.ObjectMeta.SetNamespace(policy.Spec.OperatorGroup.Namespace) - operatorGroup.Spec.TargetNamespaces = policy.Spec.OperatorGroup.Target.Namespace +// mergeObjects takes fields from the desired object and sets/merges them on the +// existing object. It checks and returns whether an update is really necessary +// with a server-side dry-run. +func (r *OperatorPolicyReconciler) mergeObjects( + ctx context.Context, + desired map[string]interface{}, + existing *unstructured.Unstructured, + complianceType string, +) (updateNeeded, updateIsForbidden bool, err error) { + desiredObj := unstructured.Unstructured{Object: desired} + + // Use a copy since some values can be directly assigned to mergedObj in handleSingleKey. + existingObjectCopy := existing.DeepCopy() + removeFieldsForComparison(existingObjectCopy) + + _, errMsg, updateNeeded, _ := handleKeys( + desiredObj, existing, existingObjectCopy, complianceType, "", false, + ) + if errMsg != "" { + return updateNeeded, false, errors.New(errMsg) } - OpLog.Info("Creating the operator group", "kind", operatorGroup.Kind, - "namespace", operatorGroup.Namespace) + if updateNeeded { + err := r.Update(ctx, existing, client.DryRunAll) + if err != nil { + if k8serrors.IsForbidden(err) { + // This indicates the update would make a change, but the change is not allowed, + // for example, the changed field might be immutable. + // The policy should be marked as noncompliant, but an enforcement update would fail. + return true, true, nil + } + + return updateNeeded, false, err + } + + removeFieldsForComparison(existing) + + if reflect.DeepEqual(existing.Object, existingObjectCopy.Object) { + // The dry run indicates that there is not *really* a mismatch. + updateNeeded = false + } + } - return operatorGroup + return updateNeeded, false, nil } diff --git a/controllers/operatorpolicy_controller_test.go b/controllers/operatorpolicy_controller_test.go index 8f37456b..80dc0918 100644 --- a/controllers/operatorpolicy_controller_test.go +++ b/controllers/operatorpolicy_controller_test.go @@ -3,9 +3,9 @@ package controllers import ( "testing" - operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" policyv1beta1 "open-cluster-management.io/config-policy-controller/api/v1beta1" @@ -21,16 +21,16 @@ func TestBuildSubscription(t *testing.T) { Severity: "low", RemediationAction: "enforce", ComplianceType: "musthave", - Subscription: policyv1beta1.SubscriptionSpec{ - SubscriptionSpec: operatorv1alpha1.SubscriptionSpec{ - Channel: "stable", - Package: "my-operator", - InstallPlanApproval: "Automatic", - CatalogSource: "my-catalog", - CatalogSourceNamespace: "my-ns", - StartingCSV: "my-operator-v1", - }, - Namespace: "default", + Subscription: runtime.RawExtension{ + Raw: []byte(`{ + "namespace": "default", + "source": "my-catalog", + "sourceNamespace": "my-ns", + "name": "my-operator", + "channel": "stable", + "startingCSV": "my-operator-v1", + "installPlanApproval": "Automatic" + }`), }, }, } @@ -41,11 +41,11 @@ func TestBuildSubscription(t *testing.T) { } // Check values are correctly bootstrapped to the Subscription - ret := buildSubscription(testPolicy) + ret, err := buildSubscription(testPolicy) + assert.Equal(t, err, nil) assert.Equal(t, ret.GroupVersionKind(), desiredGVK) assert.Equal(t, ret.ObjectMeta.Name, "my-operator") assert.Equal(t, ret.ObjectMeta.Namespace, "default") - assert.Equal(t, ret.Spec, &testPolicy.Spec.Subscription.SubscriptionSpec) } func TestBuildOperatorGroup(t *testing.T) { @@ -58,16 +58,16 @@ func TestBuildOperatorGroup(t *testing.T) { Severity: "low", RemediationAction: "enforce", ComplianceType: "musthave", - Subscription: policyv1beta1.SubscriptionSpec{ - SubscriptionSpec: operatorv1alpha1.SubscriptionSpec{ - Channel: "stable", - Package: "my-operator", - InstallPlanApproval: "Automatic", - CatalogSource: "my-catalog", - CatalogSourceNamespace: "my-ns", - StartingCSV: "my-operator-v1", - }, - Namespace: "default", + Subscription: runtime.RawExtension{ + Raw: []byte(`{ + "namespace": "default", + "source": "my-catalog", + "sourceNamespace": "my-ns", + "name": "my-operator", + "channel": "stable", + "startingCSV": "my-operator-v1", + "installPlanApproval": "Automatic" + }`), }, }, } @@ -78,9 +78,9 @@ func TestBuildOperatorGroup(t *testing.T) { } // Ensure OperatorGroup values are populated correctly - ret := buildOperatorGroup(testPolicy) + ret, err := buildOperatorGroup(testPolicy) + assert.Equal(t, err, nil) assert.Equal(t, ret.GroupVersionKind(), desiredGVK) - assert.Equal(t, ret.ObjectMeta.GetName(), "my-operator-default-og") + assert.Equal(t, ret.ObjectMeta.GetGenerateName(), "default-") assert.Equal(t, ret.ObjectMeta.GetNamespace(), "default") - assert.Equal(t, ret.Spec.TargetNamespaces, []string{}) } diff --git a/controllers/operatorpolicy_status.go b/controllers/operatorpolicy_status.go new file mode 100644 index 00000000..e89372c5 --- /dev/null +++ b/controllers/operatorpolicy_status.go @@ -0,0 +1,453 @@ +package controllers + +import ( + "context" + "fmt" + "sort" + "strings" + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/client" + + policyv1 "open-cluster-management.io/config-policy-controller/api/v1" + policyv1beta1 "open-cluster-management.io/config-policy-controller/api/v1beta1" +) + +// updateStatus takes one condition to update, and related objects for that condition. The related +// objects given will replace all existing relatedObjects with the same gvk. If a condition is +// changed, the compliance will be recalculated and a compliance event will be emitted. The +// condition and related objects can match what is already in the status - in that case, no API +// calls are made. The `lastTransitionTime` on a condition is not considered when checking if the +// condition has changed. If not provided, the `lastTransitionTime` will use "now". It also handles +// preserving the `CreatedByPolicy` property on relatedObjects. +// +// This function requires that all given related objects are of the same kind. +// +// Note that only changing the related objects will not emit a new compliance event, but will update +// the status. +func (r *OperatorPolicyReconciler) updateStatus( + ctx context.Context, + policy *policyv1beta1.OperatorPolicy, + updatedCondition metav1.Condition, + updatedRelatedObjs ...policyv1.RelatedObject, +) error { + condChanged := false + + if updatedCondition.LastTransitionTime.IsZero() { + updatedCondition.LastTransitionTime = metav1.Now() + } + + condIdx, existingCondition := policy.Status.GetCondition(updatedCondition.Type) + if condIdx == -1 { + condChanged = true + + // Just append, the conditions will be sorted later. + policy.Status.Conditions = append(policy.Status.Conditions, updatedCondition) + } else if conditionChanged(updatedCondition, existingCondition) { + condChanged = true + + policy.Status.Conditions[condIdx] = updatedCondition + } + + if condChanged { + updatedComplianceCondition := calculateComplianceCondition(policy) + + compCondIdx, _ := policy.Status.GetCondition(updatedComplianceCondition.Type) + if compCondIdx == -1 { + policy.Status.Conditions = append(policy.Status.Conditions, updatedComplianceCondition) + } else { + policy.Status.Conditions[compCondIdx] = updatedComplianceCondition + } + + // Sort the conditions based on their type. + sort.SliceStable(policy.Status.Conditions, func(i, j int) bool { + return policy.Status.Conditions[i].Type < policy.Status.Conditions[j].Type + }) + + if updatedComplianceCondition.Status == metav1.ConditionTrue { + policy.Status.ComplianceState = policyv1.Compliant + } else { + policy.Status.ComplianceState = policyv1.NonCompliant + } + + err := r.emitComplianceEvent(ctx, policy, updatedComplianceCondition) + if err != nil { + return err + } + } + + relObjsChanged := false + + prevRelObjs := make(map[int]policyv1.RelatedObject) + if len(updatedRelatedObjs) != 0 { + prevRelObjs = policy.Status.RelatedObjsOfKind(updatedRelatedObjs[0].Object.Kind) + } + + for _, prevObj := range prevRelObjs { + nameFound := false + + for i, updatedObj := range updatedRelatedObjs { + if prevObj.Object.Metadata.Name != updatedObj.Object.Metadata.Name { + continue + } + + nameFound = true + + if updatedObj.Properties != nil && prevObj.Properties != nil { + if updatedObj.Properties.UID != prevObj.Properties.UID { + relObjsChanged = true + } else if prevObj.Properties.CreatedByPolicy != nil { + // There is an assumption here that it will never need to transition to false. + updatedRelatedObjs[i].Properties.CreatedByPolicy = prevObj.Properties.CreatedByPolicy + } + } + + if prevObj.Compliant != updatedObj.Compliant || prevObj.Reason != updatedObj.Reason { + relObjsChanged = true + } + } + + if !nameFound { + relObjsChanged = true + } + } + + // Catch the case where there is a new object in updatedRelatedObjs + if len(prevRelObjs) != len(updatedRelatedObjs) { + relObjsChanged = true + } + + if relObjsChanged { + // start with the related objects which do not match the currently considered kind + newRelObjs := make([]policyv1.RelatedObject, 0) + + for idx, relObj := range policy.Status.RelatedObjects { + if _, matchedIdx := prevRelObjs[idx]; !matchedIdx { + newRelObjs = append(newRelObjs, relObj) + } + } + + // add the new related objects + newRelObjs = append(newRelObjs, updatedRelatedObjs...) + + // sort the related objects by kind and name + sort.SliceStable(newRelObjs, func(i, j int) bool { + if newRelObjs[i].Object.Kind != newRelObjs[j].Object.Kind { + return newRelObjs[i].Object.Kind < newRelObjs[j].Object.Kind + } + + return newRelObjs[i].Object.Metadata.Name < newRelObjs[j].Object.Metadata.Name + }) + + policy.Status.RelatedObjects = newRelObjs + } + + if condChanged || relObjsChanged { + return r.Status().Update(ctx, policy) + } + + return nil +} + +func conditionChanged(updatedCondition, existingCondition metav1.Condition) bool { + if updatedCondition.Message != existingCondition.Message { + return true + } + + if updatedCondition.Reason != existingCondition.Reason { + return true + } + + if updatedCondition.Status != existingCondition.Status { + return true + } + + return false +} + +// The Compliance condition is calculated by going through the known conditions in a consistent +// order, checking if there are any reasons the policy should be NonCompliant, and accumulating +// the reasons into one string to reflect the whole status. +func calculateComplianceCondition(policy *policyv1beta1.OperatorPolicy) metav1.Condition { + foundNonCompliant := false + messages := make([]string, 0) + + idx, cond := policy.Status.GetCondition(opGroupConditionType) + if idx == -1 { + messages = append(messages, "the status of the OperatorGroup is unknown") + foundNonCompliant = true + } else { + messages = append(messages, cond.Message) + + if cond.Status != metav1.ConditionTrue { + foundNonCompliant = true + } + } + + idx, cond = policy.Status.GetCondition(subConditionType) + if idx == -1 { + messages = append(messages, "the status of the Subscription is unknown") + foundNonCompliant = true + } else { + messages = append(messages, cond.Message) + + if cond.Status != metav1.ConditionTrue { + foundNonCompliant = true + } + } + + // FUTURE: check additional conditions + + if foundNonCompliant { + return metav1.Condition{ + Type: compliantConditionType, + Status: metav1.ConditionFalse, + LastTransitionTime: metav1.Now(), + Reason: "NonCompliant", + Message: "NonCompliant; " + strings.Join(messages, ", "), + } + } + + return metav1.Condition{ + Type: compliantConditionType, + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Now(), + Reason: "Compliant", + Message: "Compliant; " + strings.Join(messages, ", "), + } +} + +func (r *OperatorPolicyReconciler) emitComplianceEvent( + ctx context.Context, + policy *policyv1beta1.OperatorPolicy, + complianceCondition metav1.Condition, +) error { + if len(policy.OwnerReferences) == 0 { + return nil // there is nothing to do, since no owner is set + } + + ownerRef := policy.OwnerReferences[0] + now := time.Now() + event := &corev1.Event{ + ObjectMeta: metav1.ObjectMeta{ + // This event name matches the convention of recorders from client-go + Name: fmt.Sprintf("%v.%x", ownerRef.Name, now.UnixNano()), + Namespace: policy.Namespace, + }, + InvolvedObject: corev1.ObjectReference{ + Kind: ownerRef.Kind, + Namespace: policy.Namespace, // k8s ensures owners are always in the same namespace + Name: ownerRef.Name, + UID: ownerRef.UID, + APIVersion: ownerRef.APIVersion, + }, + Reason: fmt.Sprintf(eventFmtStr, policy.Namespace, policy.Name), + Message: complianceCondition.Message, + Source: corev1.EventSource{ + Component: ControllerName, + Host: r.InstanceName, + }, + FirstTimestamp: metav1.NewTime(now), + LastTimestamp: metav1.NewTime(now), + Count: 1, + Type: "Normal", + Action: "ComplianceStateUpdate", + Related: &corev1.ObjectReference{ + Kind: policy.Kind, + Namespace: policy.Namespace, + Name: policy.Name, + UID: policy.UID, + APIVersion: policy.APIVersion, + }, + ReportingController: ControllerName, + ReportingInstance: r.InstanceName, + } + + if policy.Status.ComplianceState != policyv1.Compliant { + event.Type = "Warning" + } + + return r.Create(ctx, event) +} + +const ( + compliantConditionType = "Compliant" + opGroupConditionType = "OperatorGroupCompliant" + subConditionType = "SubscriptionCompliant" +) + +func condType(kind string) string { + switch kind { + case "OperatorGroup": + return opGroupConditionType + case "Subscription": + return subConditionType + default: + panic("Unknown condition type for kind " + kind) + } +} + +// missingWantedCond returns a NonCompliant condition, with a Reason like '____Missing' +// and a Message like 'the ____ required by the policy was not found' +func missingWantedCond(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionFalse, + Reason: kind + "Missing", + Message: "the " + kind + " required by the policy was not found", + } +} + +// createdCond returns a Compliant condition, with a Reason like'____Created', +// and a Message like 'the ____ required by the policy was created' +func createdCond(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionTrue, + Reason: kind + "Created", + Message: "the " + kind + " required by the policy was created", + } +} + +// matchesCond returns a Compliant condition, with a Reason like'____Matches', +// and a Message like 'the ____ matches what is required by the policy' +func matchesCond(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionTrue, + Reason: kind + "Matches", + Message: "the " + kind + " matches what is required by the policy", + } +} + +// mismatchCond returns a NonCompliant condition with a Reason like '____Mismatch', +// and a Message like 'the ____ found on the cluster does not match the policy' +func mismatchCond(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionFalse, + Reason: kind + "Mismatch", + Message: "the " + kind + " found on the cluster does not match the policy", + } +} + +// mismatchCondUnfixable returns a NonCompliant condition with a Reason like '____Mismatch', +// and a Message like 'the ____ found on the cluster does not match the policy and can't be enforced' +func mismatchCondUnfixable(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionFalse, + Reason: kind + "Mismatch", + Message: "the " + kind + " found on the cluster does not match the policy and can't be enforced", + } +} + +// updatedCond returns a Compliant condition, with a Reason like'____Updated', +// and a Message like 'the ____ was updated to match the policy' +func updatedCond(kind string) metav1.Condition { + return metav1.Condition{ + Type: condType(kind), + Status: metav1.ConditionTrue, + Reason: kind + "Updated", + Message: "the " + kind + " was updated to match the policy", + } +} + +var opGroupPreexistingCond = metav1.Condition{ + Type: opGroupConditionType, + Status: metav1.ConditionTrue, + Reason: "PreexistingOperatorGroupFound", + Message: "the policy does not specify an OperatorGroup but one already exists in the namespace - " + + "assuming that OperatorGroup is correct", +} + +// opGroupTooManyCond is a NonCompliant condition with a Reason like 'TooManyOperatorGroups', +// and a Message like 'there is more than one OperatorGroup in the namespace' +var opGroupTooManyCond = metav1.Condition{ + Type: opGroupConditionType, + Status: metav1.ConditionFalse, + Reason: "TooManyOperatorGroups", + Message: "there is more than one OperatorGroup in the namespace", +} + +// missingWantedObj returns a NonCompliant RelatedObject with reason = 'Resource not found but should exist' +func missingWantedObj(obj client.Object) policyv1.RelatedObject { + return policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(obj), + Compliant: string(policyv1.NonCompliant), + Reason: reasonWantFoundDNE, + } +} + +// createdObj returns a Compliant RelatedObject with reason = 'K8s creation success' +func createdObj(obj client.Object) policyv1.RelatedObject { + created := true + + return policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(obj), + Compliant: string(policyv1.Compliant), + Reason: reasonWantFoundCreated, + Properties: &policyv1.ObjectProperties{ + CreatedByPolicy: &created, + UID: string(obj.GetUID()), + }, + } +} + +// matchedObj returns a Compliant RelatedObject with reason = 'Resource found as expected' +func matchedObj(obj client.Object) policyv1.RelatedObject { + return policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(obj), + Compliant: string(policyv1.Compliant), + Reason: reasonWantFoundExists, + Properties: &policyv1.ObjectProperties{ + UID: string(obj.GetUID()), + }, + } +} + +// mismatchedObj returns a NonCompliant RelatedObject with reason = 'Resource found but does not match' +func mismatchedObj(obj client.Object) policyv1.RelatedObject { + return policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(obj), + Compliant: string(policyv1.NonCompliant), + Reason: reasonWantFoundNoMatch, + Properties: &policyv1.ObjectProperties{ + UID: string(obj.GetUID()), + }, + } +} + +// updatedObj returns a Compliant RelatedObject with reason = 'K8s update success' +func updatedObj(obj client.Object) policyv1.RelatedObject { + return policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(obj), + Compliant: string(policyv1.Compliant), + Reason: reasonUpdateSuccess, + Properties: &policyv1.ObjectProperties{ + UID: string(obj.GetUID()), + }, + } +} + +// opGroupTooManyObjs returns a list of NonCompliant RelatedObjects, each with +// reason = 'There is more than one OperatorGroup in this namespace' +func opGroupTooManyObjs(opGroups []unstructured.Unstructured) []policyv1.RelatedObject { + objs := make([]policyv1.RelatedObject, len(opGroups)) + + for i, opGroup := range opGroups { + objs[i] = policyv1.RelatedObject{ + Object: policyv1.ObjectResourceFromObj(&opGroup), + Compliant: string(policyv1.NonCompliant), + Reason: "There is more than one OperatorGroup in this namespace", + Properties: &policyv1.ObjectProperties{ + UID: string(opGroup.GetUID()), + }, + } + } + + return objs +} diff --git a/deploy/crds/kustomize_operatorpolicy/allowed-compliance-types.json b/deploy/crds/kustomize_operatorpolicy/allowed-compliance-types.json new file mode 100644 index 00000000..55ffc812 --- /dev/null +++ b/deploy/crds/kustomize_operatorpolicy/allowed-compliance-types.json @@ -0,0 +1,7 @@ +[ + { + "op":"replace", + "path":"/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/complianceType/enum", + "value": ["musthave"] + } +] diff --git a/deploy/crds/kustomize_operatorpolicy/kustomization.yaml b/deploy/crds/kustomize_operatorpolicy/kustomization.yaml index 036c83e7..e836fde8 100644 --- a/deploy/crds/kustomize_operatorpolicy/kustomization.yaml +++ b/deploy/crds/kustomize_operatorpolicy/kustomization.yaml @@ -1,9 +1,15 @@ resources: - policy.open-cluster-management.io_operatorpolicies.yaml -# Add validation more complicated than Kubebuilder markers can provide patches: -- path: ns-selector-validation.json +# The OperatorPolicy currently only supports "musthave" +- path: allowed-compliance-types.json + target: + group: apiextensions.k8s.io + version: v1 + kind: CustomResourceDefinition + name: operatorpolicies.policy.open-cluster-management.io +- path: template-label.json target: group: apiextensions.k8s.io version: v1 diff --git a/deploy/crds/kustomize_operatorpolicy/ns-selector-validation.json b/deploy/crds/kustomize_operatorpolicy/ns-selector-validation.json deleted file mode 100644 index 9e693a0e..00000000 --- a/deploy/crds/kustomize_operatorpolicy/ns-selector-validation.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "op":"add", - "path":"/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/operatorGroup/properties/target/oneOf", - "value": [{ - "required": ["namespaces"] - },{ - "required": ["selector"] - }] - } -] diff --git a/deploy/crds/kustomize_operatorpolicy/policy.open-cluster-management.io_operatorpolicies.yaml b/deploy/crds/kustomize_operatorpolicy/policy.open-cluster-management.io_operatorpolicies.yaml index bad902fe..9bd96eb6 100644 --- a/deploy/crds/kustomize_operatorpolicy/policy.open-cluster-management.io_operatorpolicies.yaml +++ b/deploy/crds/kustomize_operatorpolicy/policy.open-cluster-management.io_operatorpolicies.yaml @@ -51,84 +51,11 @@ spec: - mustnothave type: string operatorGroup: - description: OperatorGroup requires at least 1 of target namespaces, - or label selectors to be set to scope member operators' namespaced - permissions. If both are provided, only the target namespace will - be used and the label selector will be omitted. - properties: - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent - type: string - serviceAccountName: - description: Name of the OLM ServiceAccount that defines permissions - for member operators - type: string - target: - description: Target namespaces of the referent - properties: - namespaces: - description: '''namespaces'' and ''selector'' both define - an array/set of target namespaces that should be affected - on the cluster. Only one of ''namespaces'' or ''selector'' - should be specified, and if both are set then ''selector'' - will be omitted.' - items: - type: string - type: array - selector: - description: '''namespaces'' and ''selector'' both define - an array/set of target namespaces that should be affected - on the cluster. Only one of ''namespaces'' or ''selector'' - should be specified, and if both are set then ''selector'' - will be omitted.' - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: object + description: Include the name, namespace, and any `spec` fields for + the OperatorGroup. For more info, see `kubectl explain operatorgroup.spec` + or https://olm.operatorframework.io/docs/concepts/crds/operatorgroup/ type: object + x-kubernetes-preserve-unknown-fields: true remediationAction: description: 'RemediationAction : enforce or inform' enum: @@ -137,53 +64,6 @@ spec: - Enforce - enforce type: string - removalBehavior: - description: RemovalBehavior defines resource behavior when policy - is removed - properties: - apiServiceDefinitions: - description: Kind APIServiceDefinitions - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - clusterServiceVersions: - description: Kind ClusterServiceVersion - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - customResourceDefinitions: - description: Kind CustomResourceDefinitions - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - installPlans: - description: Kind InstallPlan - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - operatorGroups: - description: Kind OperatorGroup - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - subscriptions: - description: Kind Subscription - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - type: object severity: description: 'Severity : low, medium, high, or critical' enum: @@ -196,2987 +76,12 @@ spec: - critical - Critical type: string - statusConfig: - description: StatusConfig defines how resource statuses affect the - OperatorPolicy status and compliance - properties: - catalogSourceUnhealthy: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - deploymentsUnavailable: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - upgradesAvailable: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - upgradesProgressing: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - type: object subscription: - description: Subscription defines an Application that can be installed - properties: - channel: - type: string - config: - description: SubscriptionConfig contains configuration specified - for a subscription. - properties: - affinity: - description: If specified, overrides the pod's scheduling - constraints. nil sub-attributes will *not* override the - original values in the pod.spec for those sub-attributes. - Use empty object ({}) to erase original sub-attribute values. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. - items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to an update), the system may or may not try - to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: A null or empty node selector term - matches no objects. The requirements of them - are ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of - resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to a pod label update), the system may or may - not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, - i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, - etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating through - the elements of this field and adding "weight" to - the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of - resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - anti-affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - env: - description: Env is a list of environment variables to set - in the container. Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables - in the container and any service environment variables. - If a variable cannot be resolved, the reference in - the input string will be unchanged. Double $$ are - reduced to a single $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce - the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the - variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in - the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of - the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: EnvFrom is a list of sources to populate environment - variables in the container. The keys defined within a source - must be a C_IDENTIFIER. All invalid keys will be reported - as an event when the container is starting. When a key exists - in multiple sources, the value associated with the last - source will take precedence. Values defined by an Env with - a duplicate key will take precedence. Immutable. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must - be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be - defined - type: boolean - type: object - type: object - type: array - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must be true - for the pod to fit on a node. Selector which must match - a node''s labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - resources: - description: 'Resources represents compute resources required - by this container. Immutable. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: Selector is the label selector for pods to be - configured. Existing ReplicaSets whose pods are selected - by this will be the ones affected by this deployment. It - must match the pod template's labels. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - tolerations: - description: Tolerations are the pod's tolerations. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value should - be empty, otherwise just a regular string. - type: string - type: object - type: array - volumeMounts: - description: List of VolumeMounts to set in the container. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the - volume should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts - are propagated from the host to container and the - other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's - root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves - similarly to SubPath but environment variable references - $(VAR_NAME) are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr and SubPath - are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - description: List of Volumes to set in the podSpec. - items: - description: Volume represents a named volume in a pod that - may be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS - Disk resource that is attached to a kubelet''s host - machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'readOnly value true will force the - readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'volumeID is unique ID of the persistent - disk resource in AWS (Amazon EBS volume). More - info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data Disk - mount on the host and bind mount to the pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: - None, Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk - in the blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in - the blob storage - type: string - fsType: - description: fsType is Filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single - blob disk per storage account Managed: azure - managed data disk (only in managed availability - set). defaults to shared' - type: string - readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service - mount on the host and bind mount to the pod. - properties: - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that - contains Azure Storage Account Name and Key - type: string - shareName: - description: shareName is the azure share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: cephFS represents a Ceph FS mount on the - host that shares a pod's lifetime - properties: - monitors: - description: 'monitors is Required: Monitors is - a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted - root, rather than the full Ceph tree, default - is /' - type: string - readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'secretFile is Optional: SecretFile - is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'secretRef is Optional: SecretRef is - reference to the authentication secret for User, - default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'user is optional: User is the rados - user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Examples: "ext4", "xfs", "ntfs". - Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'secretRef is optional: points to a - secret object containing parameters used to connect - to OpenStack.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeID: - description: 'volumeID used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should - populate this volume - properties: - defaultMode: - description: 'defaultMode is optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items if unspecified, each key-value - pair in the Data field of the referenced ConfigMap - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the ConfigMap, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - csi: - description: csi (Container Storage Interface) represents - ephemeral storage that is handled by certain external - CSI drivers (Beta feature). - properties: - driver: - description: driver is the name of the CSI driver - that handles this volume. Consult with your admin - for the correct name as registered in the cluster. - type: string - fsType: - description: fsType to mount. Ex. "ext4", "xfs", - "ntfs". If not provided, the empty value is passed - to the associated CSI driver which will determine - the default filesystem to apply. - type: string - nodePublishSecretRef: - description: nodePublishSecretRef is a reference - to the secret object containing sensitive information - to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no - secret is required. If the secret object contains - more than one secret, all secret references are - passed. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI driver. - Consult your driver's documentation for supported - values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about - the pod that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward API volume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing the - pod field - properties: - fieldRef: - description: 'Required: Selects a field of - the pod: only annotations, labels, name - and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits used to - set permissions on this file, must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both - octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This - might be in conflict with other options - that affect the file mode, like fsGroup, - and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' path. - Must be utf-8 encoded. The first item of - the relative path must not start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults to - "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'emptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'medium represents what type of storage - medium should back this directory. The default - is "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The - size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would - be the minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "ephemeral represents a volume that is - handled by a cluster storage driver. The volume's - lifecycle is tied to the pod that defines it - it - will be created before the pod starts, and deleted - when the pod is removed. \n Use this if: a) the volume - is only needed while the pod runs, b) features of - normal volumes like restoring from snapshot or capacity - \ tracking are needed, c) the storage driver is - specified through a storage class, and d) the storage - driver supports dynamic volume provisioning through - \ a PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection between - this volume type and PersistentVolumeClaim). \n - Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the - lifecycle of an individual pod. \n Use CSI for light-weight - local ephemeral volumes if the CSI driver is meant - to be used that way - see the documentation of the - driver for more information. \n A pod can use both - types of ephemeral volumes and persistent volumes - at the same time." - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone - PVC to provision the volume. The pod in which - this EphemeralVolumeSource is embedded will be - the owner of the PVC, i.e. the PVC will be deleted - together with the pod. The name of the PVC will - be `-` where `` - is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too - long). \n An existing PVC with that name that - is not owned by the pod will *not* be used for - the pod to avoid using an unrelated volume by - mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created - PVC is meant to be used by the pod, the PVC has - to updated with an owner reference to the pod - once the pod exists. Normally this should not - be necessary, but it may be useful when manually - reconstructing a broken cluster. \n This field - is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, - must not be nil." - properties: - metadata: - description: May contain labels and annotations - that will be copied into the PVC when creating - it. No other fields are allowed and will be - rejected during validation. - type: object - spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into - the PVC that gets created from this template. - The same fields as in a PersistentVolumeClaim - are also valid here. - properties: - accessModes: - description: 'accessModes contains the desired - access modes the volume should have. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used - to specify either: * An existing VolumeSnapshot - object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller - can support the specified data source, - it will create a new volume based on the - contents of the specified data source. - When the AnyVolumeDataSource feature gate - is enabled, dataSource contents will be - copied to dataSourceRef, and dataSourceRef - contents will be copied to dataSource - when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef - will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - required: - - kind - - name - type: object - dataSourceRef: - description: 'dataSourceRef specifies the - object from which to populate the volume - with data, if a non-empty volume is desired. - This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, - volume binding will only succeed if the - type of the specified object matches some - installed volume populator or dynamic - provisioner. This field will replace the - functionality of the dataSource field - and as such if both fields are non-empty, - they must have the same value. For backwards - compatibility, when namespace isn''t specified - in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the - same value automatically if one of them - is empty and the other is non-empty. When - namespace is specified in dataSourceRef, - dataSource isn''t set to the same value - and must be empty. There are three important - differences between dataSource and dataSourceRef: - * While dataSource only allows two specific - types of objects, dataSourceRef allows - any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed - values (dropping them), dataSourceRef preserves - all values, and generates an error if - a disallowed value is specified. * While - dataSource only allows local objects, - dataSourceRef allows objects in any - namespaces. (Beta) Using this field requires - the AnyVolumeDataSource feature gate to - be enabled. (Alpha) Using the namespace - field of dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - namespace: - description: Namespace is the namespace - of resource being referenced Note - that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent - namespace to allow that namespace's - owner to accept the reference. See - the ReferenceGrant documentation for - details. (Alpha) This field requires - the CrossNamespaceVolumeDataSource - feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum - resources the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to - specify resource requirements that are - lower than previous value but must still - be higher than capacity recorded in the - status field of the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names - of resources, defined in spec.resourceClaims, - that are used by this container. \n - This is an alpha field and requires - enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the - name of one entry in pod.spec.resourceClaims - of the Pod where this field - is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the - minimum amount of compute resources - required. If Requests is omitted for - a container, it defaults to Limits - if that is explicitly specified, otherwise - to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over - volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - storageClassName: - description: 'storageClassName is the name - of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type - of volume is required by the claim. Value - of Filesystem is implied when not included - in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource - that is attached to a kubelet's host machine and then - exposed to the pod. - properties: - fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. TODO: how - do we prevent errors in the filesystem from compromising - the machine' - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target - worldwide names (WWNs)' - items: - type: string - type: array - wwids: - description: 'wwids Optional: FC volume world wide - identifiers (wwids) Either wwids or combination - of targetWWNs and lun must be set, but not both - simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: flexVolume represents a generic volume - resource that is provisioned/attached using an exec - based plugin. - properties: - driver: - description: driver is the name of the driver to - use for this volume. - type: string - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". The - default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds - extra command options if any.' - type: object - readOnly: - description: 'readOnly is Optional: defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'secretRef is Optional: secretRef is - reference to the secret object containing sensitive - information to pass to the plugin scripts. This - may be empty if no secret object is specified. - If the secret object contains more than one secret, - all secrets are passed to the plugin scripts.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached - to a kubelet's host machine. This depends on the Flocker - control service being running - properties: - datasetName: - description: datasetName is Name of the dataset - stored as metadata -> name on the dataset for - Flocker should be considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. - This is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'fsType is filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'pdName is unique name of the PD resource - in GCE. Used to identify the disk in GCE. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'gitRepo represents a git repository at - a particular revision. DEPRECATED: GitRepo is deprecated. - To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo - using git, then mount the EmptyDir into the Pod''s - container.' - properties: - directory: - description: directory is the target directory name. - Must not contain or start with '..'. If '.' is - supplied, the volume directory will be the git - repository. Otherwise, if specified, the volume - will contain the git repository in the subdirectory - with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the - specified revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'glusterfs represents a Glusterfs mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'endpoints is the endpoint name that - details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'readOnly here will force the Glusterfs - volume to be mounted with read-only permissions. - Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'hostPath represents a pre-existing file - or directory on the host machine that is directly - exposed to the container. This is generally used for - system agents or other privileged things that are - allowed to see the host machine. Most containers will - NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use - host directory mounts and who can/can not mount host - directories as read/write.' - properties: - path: - description: 'path of the directory on the host. - If the path is a symlink, it will follow the link - to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'iscsi represents an ISCSI Disk resource - that is attached to a kubelet''s host machine and - then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support - iSCSI Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support - iSCSI Session CHAP authentication - type: boolean - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iscsiInterface is the interface Name - that uses an iSCSI transport. Defaults to 'default' - (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: portals is the iSCSI Target Portal - List. The portal is either an IP or ip_addr:port - if the port is other than default (typically TCP - ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI - target and initiator authentication - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - targetPortal: - description: targetPortal is iSCSI Target Portal. - The Portal is either an IP or ip_addr:port if - the port is other than default (typically TCP - ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'name of the volume. Must be a DNS_LABEL - and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'nfs represents an NFS mount on the host - that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'readOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'server is the hostname or IP address - of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same - namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: readOnly Will force the ReadOnly setting - in VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon - Controller persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume - attached and mounted on kubelets host machine - properties: - fsType: - description: fSType represents the filesystem type - to mount Must be a filesystem type supported by - the host operating system. Ex. "ext4", "xfs". - Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx - volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources - secrets, configmaps, and downward API - properties: - defaultMode: - description: defaultMode are the mode bits used - to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path - are not affected by this setting. This might be - in conflict with other options that affect the - file mode, like fsGroup, and the result can be - other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected - along with other supported volume types - properties: - configMap: - description: configMap information about the - configMap data to project - properties: - items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced ConfigMap will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: optional specify whether - the ConfigMap or its keys must be defined - type: boolean - type: object - downwardAPI: - description: downwardAPI information about - the downwardAPI data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects - a field of the pod: only annotations, - labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits - used to set permissions on this - file, must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file - to be created. Must not be absolute - or contain the ''..'' path. Must - be utf-8 encoded. The first item - of the relative path must not - start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, requests.cpu and - requests.memory) are currently - supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the - secret data to project - properties: - items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced Secret will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the Secret, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: optional field specify whether - the Secret or its key must be defined - type: boolean - type: object - serviceAccountToken: - description: serviceAccountToken is information - about the serviceAccountToken data to project - properties: - audience: - description: audience is the intended - audience of the token. A recipient of - a token must identify itself with an - identifier specified in the audience - of the token, and otherwise should reject - the token. The audience defaults to - the identifier of the apiserver. - type: string - expirationSeconds: - description: expirationSeconds is the - requested duration of validity of the - service account token. As the token - approaches expiration, the kubelet volume - plugin will proactively rotate the service - account token. The kubelet will start - trying to rotate the token if the token - is older than 80 percent of its time - to live or if the token is older than - 24 hours.Defaults to 1 hour and must - be at least 10 minutes. - format: int64 - type: integer - path: - description: path is the path relative - to the mount point of the file to project - the token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the - host that shares a pod's lifetime - properties: - group: - description: group to map volume access to Default - is no group - type: string - readOnly: - description: readOnly here will force the Quobyte - volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: registry represents a single or multiple - Quobyte Registry services specified as a string - as host:port pair (multiple entries are separated - with commas) which acts as the central registry - for volumes - type: string - tenant: - description: tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned - Quobyte volumes, value is set by the plugin - type: string - user: - description: user to map volume access to Defaults - to serivceaccount user - type: string - volume: - description: volume is a string that references - an already created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'rbd represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - image: - description: 'image is the rados image name. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'keyring is the path to key ring for - RBDUser. Default is /etc/ceph/keyring. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'pool is the rados pool name. Default - is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'user is the rados user name. Default - is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent - volume attached and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Default - is "xfs". - type: string - gateway: - description: gateway is the host address of the - ScaleIO API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the - ScaleIO Protection Domain for the configured storage. - type: string - readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretRef: - description: secretRef references to the secret - for ScaleIO user and other sensitive information. - If this is not provided, Login operation will - fail. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - sslEnabled: - description: sslEnabled Flag enable/disable SSL - communication with Gateway, default false - type: boolean - storageMode: - description: storageMode indicates whether the storage - for a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage - Pool associated with the protection domain. - type: string - system: - description: system is the name of the storage system - as configured in ScaleIO. - type: string - volumeName: - description: volumeName is the name of a volume - already created in the ScaleIO system that is - associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'secret represents a secret that should - populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'defaultMode is Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items If unspecified, each key-value - pair in the Data field of the referenced Secret - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the Secret, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the - Secret or its keys must be defined - type: boolean - secretName: - description: 'secretName is the name of the secret - in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretRef: - description: secretRef specifies the secret to use - for obtaining the StorageOS API credentials. If - not specified, default values will be attempted. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeName: - description: volumeName is the human-readable name - of the StorageOS volume. Volume names are only - unique within a namespace. - type: string - volumeNamespace: - description: volumeNamespace specifies the scope - of the volume within StorageOS. If no namespace - is specified then the Pod's namespace will be - used. This allows the Kubernetes name scoping - to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default - behaviour. Set to "default" if you are not using - namespaces within StorageOS. Namespaces that do - not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume - attached and mounted on kubelets host machine - properties: - fsType: - description: fsType is filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy - Based Management (SPBM) profile ID associated - with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy - Based Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies - vSphere volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - installPlanApproval: - description: Approval is the user approval policy for an InstallPlan. - It must be one of "Automatic" or "Manual". - type: string - name: - type: string - namespace: - description: Namespace of the referent - type: string - source: - type: string - sourceNamespace: - type: string - startingCSV: - type: string - required: - - name - - source - - sourceNamespace + description: Include the namespace, and any `spec` fields for the + Subscription. For more info, see `kubectl explain subscription.spec` + or https://olm.operatorframework.io/docs/concepts/crds/subscription/ type: object + x-kubernetes-preserve-unknown-fields: true versions: description: Versions is a list of nonempty strings that specifies which installed versions are compliant when in 'inform' mode, and @@ -3266,48 +171,53 @@ spec: - type type: object type: array - relatedObject: + relatedObjects: description: List of resources processed by the policy - properties: - compliant: - type: string - object: - description: ObjectResource is an object identified by the policy - as a resource that needs to be validated. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - description: Metadata values from the referent. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - type: object - type: object + items: + description: RelatedObject is the list of objects matched by this + Policy resource. properties: + compliant: + type: string + object: + description: ObjectResource is an object identified by the policy + as a resource that needs to be validated. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Metadata values from the referent. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + type: object + type: object properties: - createdByPolicy: - description: Whether the object was created by the parent - policy - type: boolean - uid: - description: Store object UID to help track object ownership - for deletion - type: string - type: object - reason: - type: string - type: object + properties: + createdByPolicy: + description: Whether the object was created by the parent + policy + type: boolean + uid: + description: Store object UID to help track object ownership + for deletion + type: string + type: object + reason: + type: string + type: object + type: array required: - - relatedObject + - relatedObjects type: object type: object served: true diff --git a/deploy/crds/kustomize_operatorpolicy/template-label.json b/deploy/crds/kustomize_operatorpolicy/template-label.json new file mode 100644 index 00000000..7d9cc120 --- /dev/null +++ b/deploy/crds/kustomize_operatorpolicy/template-label.json @@ -0,0 +1,7 @@ +[ + { + "op":"add", + "path":"/metadata/labels", + "value": {"policy.open-cluster-management.io/policy-type": "template"} + } +] diff --git a/deploy/crds/policy.open-cluster-management.io_operatorpolicies.yaml b/deploy/crds/policy.open-cluster-management.io_operatorpolicies.yaml index a8e2d0a7..54b4bbf1 100644 --- a/deploy/crds/policy.open-cluster-management.io_operatorpolicies.yaml +++ b/deploy/crds/policy.open-cluster-management.io_operatorpolicies.yaml @@ -6,6 +6,8 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null + labels: + policy.open-cluster-management.io/policy-type: template name: operatorpolicies.policy.open-cluster-management.io spec: group: policy.open-cluster-management.io @@ -40,100 +42,14 @@ spec: description: ComplianceType describes whether we must or must not have a given resource enum: - - MustHave - - Musthave - musthave - - MustOnlyHave - - Mustonlyhave - - mustonlyhave - - MustNotHave - - Mustnothave - - mustnothave type: string operatorGroup: - description: OperatorGroup requires at least 1 of target namespaces, - or label selectors to be set to scope member operators' namespaced - permissions. If both are provided, only the target namespace will - be used and the label selector will be omitted. - properties: - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent - type: string - serviceAccountName: - description: Name of the OLM ServiceAccount that defines permissions - for member operators - type: string - target: - description: Target namespaces of the referent - oneOf: - - required: - - namespaces - - required: - - selector - properties: - namespaces: - description: '''namespaces'' and ''selector'' both define - an array/set of target namespaces that should be affected - on the cluster. Only one of ''namespaces'' or ''selector'' - should be specified, and if both are set then ''selector'' - will be omitted.' - items: - type: string - type: array - selector: - description: '''namespaces'' and ''selector'' both define - an array/set of target namespaces that should be affected - on the cluster. Only one of ''namespaces'' or ''selector'' - should be specified, and if both are set then ''selector'' - will be omitted.' - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: object + description: Include the name, namespace, and any `spec` fields for + the OperatorGroup. For more info, see `kubectl explain operatorgroup.spec` + or https://olm.operatorframework.io/docs/concepts/crds/operatorgroup/ type: object + x-kubernetes-preserve-unknown-fields: true remediationAction: description: 'RemediationAction : enforce or inform' enum: @@ -142,53 +58,6 @@ spec: - Enforce - enforce type: string - removalBehavior: - description: RemovalBehavior defines resource behavior when policy - is removed - properties: - apiServiceDefinitions: - description: Kind APIServiceDefinitions - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - clusterServiceVersions: - description: Kind ClusterServiceVersion - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - customResourceDefinitions: - description: Kind CustomResourceDefinitions - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - installPlans: - description: Kind InstallPlan - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - operatorGroups: - description: Kind OperatorGroup - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - subscriptions: - description: Kind Subscription - enum: - - Keep - - Delete - - DeleteIfUnused - type: string - type: object severity: description: 'Severity : low, medium, high, or critical' enum: @@ -201,2987 +70,12 @@ spec: - critical - Critical type: string - statusConfig: - description: StatusConfig defines how resource statuses affect the - OperatorPolicy status and compliance - properties: - catalogSourceUnhealthy: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - deploymentsUnavailable: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - upgradesAvailable: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - upgradesProgressing: - description: 'StatusConfigAction : StatusMessageOnly or NonCompliant' - enum: - - StatusMessageOnly - - NonCompliant - type: string - type: object subscription: - description: Subscription defines an Application that can be installed - properties: - channel: - type: string - config: - description: SubscriptionConfig contains configuration specified - for a subscription. - properties: - affinity: - description: If specified, overrides the pod's scheduling - constraints. nil sub-attributes will *not* override the - original values in the pod.spec for those sub-attributes. - Use empty object ({}) to erase original sub-attribute values. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. - items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to an update), the system may or may not try - to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: A null or empty node selector term - matches no objects. The requirements of them - are ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of - resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to a pod label update), the system may or may - not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, - i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, - etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating through - the elements of this field and adding "weight" to - the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of - resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - anti-affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - env: - description: Env is a list of environment variables to set - in the container. Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables - in the container and any service environment variables. - If a variable cannot be resolved, the reference in - the input string will be unchanged. Double $$ are - reduced to a single $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce - the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the - variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in - the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of - the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: EnvFrom is a list of sources to populate environment - variables in the container. The keys defined within a source - must be a C_IDENTIFIER. All invalid keys will be reported - as an event when the container is starting. When a key exists - in multiple sources, the value associated with the last - source will take precedence. Values defined by an Env with - a duplicate key will take precedence. Immutable. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must - be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be - defined - type: boolean - type: object - type: object - type: array - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must be true - for the pod to fit on a node. Selector which must match - a node''s labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - resources: - description: 'Resources represents compute resources required - by this container. Immutable. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: Selector is the label selector for pods to be - configured. Existing ReplicaSets whose pods are selected - by this will be the ones affected by this deployment. It - must match the pod template's labels. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - tolerations: - description: Tolerations are the pod's tolerations. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value should - be empty, otherwise just a regular string. - type: string - type: object - type: array - volumeMounts: - description: List of VolumeMounts to set in the container. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the - volume should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts - are propagated from the host to container and the - other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's - root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves - similarly to SubPath but environment variable references - $(VAR_NAME) are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr and SubPath - are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - description: List of Volumes to set in the podSpec. - items: - description: Volume represents a named volume in a pod that - may be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS - Disk resource that is attached to a kubelet''s host - machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'readOnly value true will force the - readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'volumeID is unique ID of the persistent - disk resource in AWS (Amazon EBS volume). More - info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data Disk - mount on the host and bind mount to the pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: - None, Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk - in the blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in - the blob storage - type: string - fsType: - description: fsType is Filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single - blob disk per storage account Managed: azure - managed data disk (only in managed availability - set). defaults to shared' - type: string - readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service - mount on the host and bind mount to the pod. - properties: - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that - contains Azure Storage Account Name and Key - type: string - shareName: - description: shareName is the azure share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: cephFS represents a Ceph FS mount on the - host that shares a pod's lifetime - properties: - monitors: - description: 'monitors is Required: Monitors is - a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted - root, rather than the full Ceph tree, default - is /' - type: string - readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'secretFile is Optional: SecretFile - is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'secretRef is Optional: SecretRef is - reference to the authentication secret for User, - default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'user is optional: User is the rados - user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Examples: "ext4", "xfs", "ntfs". - Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'secretRef is optional: points to a - secret object containing parameters used to connect - to OpenStack.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeID: - description: 'volumeID used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should - populate this volume - properties: - defaultMode: - description: 'defaultMode is optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items if unspecified, each key-value - pair in the Data field of the referenced ConfigMap - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the ConfigMap, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - csi: - description: csi (Container Storage Interface) represents - ephemeral storage that is handled by certain external - CSI drivers (Beta feature). - properties: - driver: - description: driver is the name of the CSI driver - that handles this volume. Consult with your admin - for the correct name as registered in the cluster. - type: string - fsType: - description: fsType to mount. Ex. "ext4", "xfs", - "ntfs". If not provided, the empty value is passed - to the associated CSI driver which will determine - the default filesystem to apply. - type: string - nodePublishSecretRef: - description: nodePublishSecretRef is a reference - to the secret object containing sensitive information - to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no - secret is required. If the secret object contains - more than one secret, all secret references are - passed. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI driver. - Consult your driver's documentation for supported - values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about - the pod that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward API volume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing the - pod field - properties: - fieldRef: - description: 'Required: Selects a field of - the pod: only annotations, labels, name - and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits used to - set permissions on this file, must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both - octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This - might be in conflict with other options - that affect the file mode, like fsGroup, - and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' path. - Must be utf-8 encoded. The first item of - the relative path must not start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults to - "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'emptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'medium represents what type of storage - medium should back this directory. The default - is "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The - size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would - be the minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "ephemeral represents a volume that is - handled by a cluster storage driver. The volume's - lifecycle is tied to the pod that defines it - it - will be created before the pod starts, and deleted - when the pod is removed. \n Use this if: a) the volume - is only needed while the pod runs, b) features of - normal volumes like restoring from snapshot or capacity - \ tracking are needed, c) the storage driver is - specified through a storage class, and d) the storage - driver supports dynamic volume provisioning through - \ a PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection between - this volume type and PersistentVolumeClaim). \n - Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the - lifecycle of an individual pod. \n Use CSI for light-weight - local ephemeral volumes if the CSI driver is meant - to be used that way - see the documentation of the - driver for more information. \n A pod can use both - types of ephemeral volumes and persistent volumes - at the same time." - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone - PVC to provision the volume. The pod in which - this EphemeralVolumeSource is embedded will be - the owner of the PVC, i.e. the PVC will be deleted - together with the pod. The name of the PVC will - be `-` where `` - is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too - long). \n An existing PVC with that name that - is not owned by the pod will *not* be used for - the pod to avoid using an unrelated volume by - mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created - PVC is meant to be used by the pod, the PVC has - to updated with an owner reference to the pod - once the pod exists. Normally this should not - be necessary, but it may be useful when manually - reconstructing a broken cluster. \n This field - is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, - must not be nil." - properties: - metadata: - description: May contain labels and annotations - that will be copied into the PVC when creating - it. No other fields are allowed and will be - rejected during validation. - type: object - spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into - the PVC that gets created from this template. - The same fields as in a PersistentVolumeClaim - are also valid here. - properties: - accessModes: - description: 'accessModes contains the desired - access modes the volume should have. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used - to specify either: * An existing VolumeSnapshot - object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller - can support the specified data source, - it will create a new volume based on the - contents of the specified data source. - When the AnyVolumeDataSource feature gate - is enabled, dataSource contents will be - copied to dataSourceRef, and dataSourceRef - contents will be copied to dataSource - when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef - will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - required: - - kind - - name - type: object - dataSourceRef: - description: 'dataSourceRef specifies the - object from which to populate the volume - with data, if a non-empty volume is desired. - This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, - volume binding will only succeed if the - type of the specified object matches some - installed volume populator or dynamic - provisioner. This field will replace the - functionality of the dataSource field - and as such if both fields are non-empty, - they must have the same value. For backwards - compatibility, when namespace isn''t specified - in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the - same value automatically if one of them - is empty and the other is non-empty. When - namespace is specified in dataSourceRef, - dataSource isn''t set to the same value - and must be empty. There are three important - differences between dataSource and dataSourceRef: - * While dataSource only allows two specific - types of objects, dataSourceRef allows - any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed - values (dropping them), dataSourceRef preserves - all values, and generates an error if - a disallowed value is specified. * While - dataSource only allows local objects, - dataSourceRef allows objects in any - namespaces. (Beta) Using this field requires - the AnyVolumeDataSource feature gate to - be enabled. (Alpha) Using the namespace - field of dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - namespace: - description: Namespace is the namespace - of resource being referenced Note - that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent - namespace to allow that namespace's - owner to accept the reference. See - the ReferenceGrant documentation for - details. (Alpha) This field requires - the CrossNamespaceVolumeDataSource - feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum - resources the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to - specify resource requirements that are - lower than previous value but must still - be higher than capacity recorded in the - status field of the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names - of resources, defined in spec.resourceClaims, - that are used by this container. \n - This is an alpha field and requires - enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the - name of one entry in pod.spec.resourceClaims - of the Pod where this field - is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the - minimum amount of compute resources - required. If Requests is omitted for - a container, it defaults to Limits - if that is explicitly specified, otherwise - to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over - volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - storageClassName: - description: 'storageClassName is the name - of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type - of volume is required by the claim. Value - of Filesystem is implied when not included - in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource - that is attached to a kubelet's host machine and then - exposed to the pod. - properties: - fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. TODO: how - do we prevent errors in the filesystem from compromising - the machine' - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target - worldwide names (WWNs)' - items: - type: string - type: array - wwids: - description: 'wwids Optional: FC volume world wide - identifiers (wwids) Either wwids or combination - of targetWWNs and lun must be set, but not both - simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: flexVolume represents a generic volume - resource that is provisioned/attached using an exec - based plugin. - properties: - driver: - description: driver is the name of the driver to - use for this volume. - type: string - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". The - default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds - extra command options if any.' - type: object - readOnly: - description: 'readOnly is Optional: defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'secretRef is Optional: secretRef is - reference to the secret object containing sensitive - information to pass to the plugin scripts. This - may be empty if no secret object is specified. - If the secret object contains more than one secret, - all secrets are passed to the plugin scripts.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached - to a kubelet's host machine. This depends on the Flocker - control service being running - properties: - datasetName: - description: datasetName is Name of the dataset - stored as metadata -> name on the dataset for - Flocker should be considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. - This is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'fsType is filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'pdName is unique name of the PD resource - in GCE. Used to identify the disk in GCE. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'gitRepo represents a git repository at - a particular revision. DEPRECATED: GitRepo is deprecated. - To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo - using git, then mount the EmptyDir into the Pod''s - container.' - properties: - directory: - description: directory is the target directory name. - Must not contain or start with '..'. If '.' is - supplied, the volume directory will be the git - repository. Otherwise, if specified, the volume - will contain the git repository in the subdirectory - with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the - specified revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'glusterfs represents a Glusterfs mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'endpoints is the endpoint name that - details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'readOnly here will force the Glusterfs - volume to be mounted with read-only permissions. - Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'hostPath represents a pre-existing file - or directory on the host machine that is directly - exposed to the container. This is generally used for - system agents or other privileged things that are - allowed to see the host machine. Most containers will - NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use - host directory mounts and who can/can not mount host - directories as read/write.' - properties: - path: - description: 'path of the directory on the host. - If the path is a symlink, it will follow the link - to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'iscsi represents an ISCSI Disk resource - that is attached to a kubelet''s host machine and - then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support - iSCSI Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support - iSCSI Session CHAP authentication - type: boolean - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iscsiInterface is the interface Name - that uses an iSCSI transport. Defaults to 'default' - (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: portals is the iSCSI Target Portal - List. The portal is either an IP or ip_addr:port - if the port is other than default (typically TCP - ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI - target and initiator authentication - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - targetPortal: - description: targetPortal is iSCSI Target Portal. - The Portal is either an IP or ip_addr:port if - the port is other than default (typically TCP - ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'name of the volume. Must be a DNS_LABEL - and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'nfs represents an NFS mount on the host - that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'readOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'server is the hostname or IP address - of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same - namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: readOnly Will force the ReadOnly setting - in VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon - Controller persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume - attached and mounted on kubelets host machine - properties: - fsType: - description: fSType represents the filesystem type - to mount Must be a filesystem type supported by - the host operating system. Ex. "ext4", "xfs". - Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx - volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources - secrets, configmaps, and downward API - properties: - defaultMode: - description: defaultMode are the mode bits used - to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path - are not affected by this setting. This might be - in conflict with other options that affect the - file mode, like fsGroup, and the result can be - other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected - along with other supported volume types - properties: - configMap: - description: configMap information about the - configMap data to project - properties: - items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced ConfigMap will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: optional specify whether - the ConfigMap or its keys must be defined - type: boolean - type: object - downwardAPI: - description: downwardAPI information about - the downwardAPI data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects - a field of the pod: only annotations, - labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits - used to set permissions on this - file, must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file - to be created. Must not be absolute - or contain the ''..'' path. Must - be utf-8 encoded. The first item - of the relative path must not - start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, requests.cpu and - requests.memory) are currently - supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the - secret data to project - properties: - items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced Secret will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the Secret, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: optional field specify whether - the Secret or its key must be defined - type: boolean - type: object - serviceAccountToken: - description: serviceAccountToken is information - about the serviceAccountToken data to project - properties: - audience: - description: audience is the intended - audience of the token. A recipient of - a token must identify itself with an - identifier specified in the audience - of the token, and otherwise should reject - the token. The audience defaults to - the identifier of the apiserver. - type: string - expirationSeconds: - description: expirationSeconds is the - requested duration of validity of the - service account token. As the token - approaches expiration, the kubelet volume - plugin will proactively rotate the service - account token. The kubelet will start - trying to rotate the token if the token - is older than 80 percent of its time - to live or if the token is older than - 24 hours.Defaults to 1 hour and must - be at least 10 minutes. - format: int64 - type: integer - path: - description: path is the path relative - to the mount point of the file to project - the token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the - host that shares a pod's lifetime - properties: - group: - description: group to map volume access to Default - is no group - type: string - readOnly: - description: readOnly here will force the Quobyte - volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: registry represents a single or multiple - Quobyte Registry services specified as a string - as host:port pair (multiple entries are separated - with commas) which acts as the central registry - for volumes - type: string - tenant: - description: tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned - Quobyte volumes, value is set by the plugin - type: string - user: - description: user to map volume access to Defaults - to serivceaccount user - type: string - volume: - description: volume is a string that references - an already created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'rbd represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - image: - description: 'image is the rados image name. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'keyring is the path to key ring for - RBDUser. Default is /etc/ceph/keyring. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'pool is the rados pool name. Default - is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'user is the rados user name. Default - is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent - volume attached and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Default - is "xfs". - type: string - gateway: - description: gateway is the host address of the - ScaleIO API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the - ScaleIO Protection Domain for the configured storage. - type: string - readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretRef: - description: secretRef references to the secret - for ScaleIO user and other sensitive information. - If this is not provided, Login operation will - fail. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - sslEnabled: - description: sslEnabled Flag enable/disable SSL - communication with Gateway, default false - type: boolean - storageMode: - description: storageMode indicates whether the storage - for a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage - Pool associated with the protection domain. - type: string - system: - description: system is the name of the storage system - as configured in ScaleIO. - type: string - volumeName: - description: volumeName is the name of a volume - already created in the ScaleIO system that is - associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'secret represents a secret that should - populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'defaultMode is Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items If unspecified, each key-value - pair in the Data field of the referenced Secret - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the Secret, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the - Secret or its keys must be defined - type: boolean - secretName: - description: 'secretName is the name of the secret - in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. - type: boolean - secretRef: - description: secretRef specifies the secret to use - for obtaining the StorageOS API credentials. If - not specified, default values will be attempted. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeName: - description: volumeName is the human-readable name - of the StorageOS volume. Volume names are only - unique within a namespace. - type: string - volumeNamespace: - description: volumeNamespace specifies the scope - of the volume within StorageOS. If no namespace - is specified then the Pod's namespace will be - used. This allows the Kubernetes name scoping - to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default - behaviour. Set to "default" if you are not using - namespaces within StorageOS. Namespaces that do - not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume - attached and mounted on kubelets host machine - properties: - fsType: - description: fsType is filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy - Based Management (SPBM) profile ID associated - with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy - Based Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies - vSphere volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - installPlanApproval: - description: Approval is the user approval policy for an InstallPlan. - It must be one of "Automatic" or "Manual". - type: string - name: - type: string - namespace: - description: Namespace of the referent - type: string - source: - type: string - sourceNamespace: - type: string - startingCSV: - type: string - required: - - name - - source - - sourceNamespace + description: Include the namespace, and any `spec` fields for the + Subscription. For more info, see `kubectl explain subscription.spec` + or https://olm.operatorframework.io/docs/concepts/crds/subscription/ type: object + x-kubernetes-preserve-unknown-fields: true versions: description: Versions is a list of nonempty strings that specifies which installed versions are compliant when in 'inform' mode, and @@ -3271,48 +165,53 @@ spec: - type type: object type: array - relatedObject: + relatedObjects: description: List of resources processed by the policy - properties: - compliant: - type: string - object: - description: ObjectResource is an object identified by the policy - as a resource that needs to be validated. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - description: Metadata values from the referent. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - type: object - type: object + items: + description: RelatedObject is the list of objects matched by this + Policy resource. properties: + compliant: + type: string + object: + description: ObjectResource is an object identified by the policy + as a resource that needs to be validated. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Metadata values from the referent. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + type: object + type: object properties: - createdByPolicy: - description: Whether the object was created by the parent - policy - type: boolean - uid: - description: Store object UID to help track object ownership - for deletion - type: string - type: object - reason: - type: string - type: object + properties: + createdByPolicy: + description: Whether the object was created by the parent + policy + type: boolean + uid: + description: Store object UID to help track object ownership + for deletion + type: string + type: object + reason: + type: string + type: object + type: array required: - - relatedObject + - relatedObjects type: object type: object served: true diff --git a/main.go b/main.go index 91fde207..92a5d1a2 100644 --- a/main.go +++ b/main.go @@ -434,6 +434,7 @@ func main() { OpReconciler := controllers.OperatorPolicyReconciler{ Client: mgr.GetClient(), DynamicWatcher: watcher, + InstanceName: instanceName, } if err = OpReconciler.SetupWithManager(mgr, depEvents); err != nil { diff --git a/test/e2e/case15_event_format_test.go b/test/e2e/case15_event_format_test.go index f620d1df..306f2f6a 100644 --- a/test/e2e/case15_event_format_test.go +++ b/test/e2e/case15_event_format_test.go @@ -39,8 +39,8 @@ const ( var _ = Describe("Testing compliance event formatting", Ordered, func() { It("Records the right events for a policy that is always compliant", func() { - createConfigPolicyWithParent(case15AlwaysCompliantParentYaml, case15AlwaysCompliantParentName, - case15AlwaysCompliantYaml) + createObjWithParent(case15AlwaysCompliantParentYaml, case15AlwaysCompliantParentName, + case15AlwaysCompliantYaml, testNamespace, gvrPolicy, gvrConfigPolicy) plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, case15AlwaysCompliantName, testNamespace, true, defaultTimeoutSeconds) @@ -73,8 +73,8 @@ var _ = Describe("Testing compliance event formatting", Ordered, func() { Expect(nonCompParentEvents).To(BeEmpty()) }) It("Records the right events for a policy that is never compliant", func() { - createConfigPolicyWithParent(case15NeverCompliantParentYaml, case15NeverCompliantParentName, - case15NeverCompliantYaml) + createObjWithParent(case15NeverCompliantParentYaml, case15NeverCompliantParentName, + case15NeverCompliantYaml, testNamespace, gvrPolicy, gvrConfigPolicy) plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, case15NeverCompliantName, testNamespace, true, defaultTimeoutSeconds) @@ -107,8 +107,8 @@ var _ = Describe("Testing compliance event formatting", Ordered, func() { }, defaultTimeoutSeconds, 1).ShouldNot(BeEmpty()) }) It("Records events for a policy that becomes compliant", func() { - createConfigPolicyWithParent(case15BecomesCompliantParentYaml, case15BecomesCompliantParentName, - case15BecomesCompliantYaml) + createObjWithParent(case15BecomesCompliantParentYaml, case15BecomesCompliantParentName, + case15BecomesCompliantYaml, testNamespace, gvrPolicy, gvrConfigPolicy) plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, case15BecomesCompliantName, testNamespace, true, defaultTimeoutSeconds) @@ -147,8 +147,8 @@ var _ = Describe("Testing compliance event formatting", Ordered, func() { }, defaultTimeoutSeconds, 1).ShouldNot(BeEmpty()) }) It("Records events for a policy that becomes noncompliant", func() { - createConfigPolicyWithParent(case15BecomesNonCompliantParentYaml, case15BecomesNonCompliantParentName, - case15BecomesNonCompliantYaml) + createObjWithParent(case15BecomesNonCompliantParentYaml, case15BecomesNonCompliantParentName, + case15BecomesNonCompliantYaml, testNamespace, gvrPolicy, gvrConfigPolicy) plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, case15BecomesNonCompliantName, testNamespace, true, defaultTimeoutSeconds) diff --git a/test/e2e/case17_evaluation_interval_test.go b/test/e2e/case17_evaluation_interval_test.go index 44df988b..9ab9b288 100644 --- a/test/e2e/case17_evaluation_interval_test.go +++ b/test/e2e/case17_evaluation_interval_test.go @@ -29,7 +29,8 @@ const ( var _ = Describe("Test evaluation interval", Ordered, func() { It("Verifies that status.lastEvaluated is properly set", func() { - createConfigPolicyWithParent(case17ParentPolicy, case17ParentPolicyName, case17Policy) + createObjWithParent(case17ParentPolicy, case17ParentPolicyName, + case17Policy, testNamespace, gvrPolicy, gvrConfigPolicy) By("Getting status.lastEvaluated") var managedPlc *unstructured.Unstructured diff --git a/test/e2e/case21_alternative_kubeconfig_test.go b/test/e2e/case21_alternative_kubeconfig_test.go index e3d09469..6155274b 100644 --- a/test/e2e/case21_alternative_kubeconfig_test.go +++ b/test/e2e/case21_alternative_kubeconfig_test.go @@ -59,7 +59,7 @@ var _ = Describe("Test an alternative kubeconfig for policy evaluation", Ordered }) It("should create the namespace using the alternative kubeconfig", func() { - createConfigPolicyWithParent(parentPolicyYAML, parentPolicyName, policyYAML) + createObjWithParent(parentPolicyYAML, parentPolicyName, policyYAML, testNamespace, gvrPolicy, gvrConfigPolicy) By("Verifying that the " + policyName + " policy is compliant") Eventually(func() interface{} { diff --git a/test/e2e/case31_policy_history_test.go b/test/e2e/case31_policy_history_test.go index 6e6f7681..cc0a8664 100644 --- a/test/e2e/case31_policy_history_test.go +++ b/test/e2e/case31_policy_history_test.go @@ -10,6 +10,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "open-cluster-management.io/config-policy-controller/test/utils" ) @@ -55,7 +56,7 @@ var _ = Describe("Test policy history messages when KubeAPI omits values in the ) It("sets up a configuration policy with an omitempty boolean set to false", func() { - createConfigPolicyWithParent(policyYAML, policyName, configPolicyYAML) + createObjWithParent(policyYAML, policyName, configPolicyYAML, testNamespace, gvrPolicy, gvrConfigPolicy) }) It("checks the policy's history", func() { @@ -82,7 +83,7 @@ var _ = Describe("Test policy history messages when KubeAPI omits values in the ) It("sets up a configuration policy with an omitempty number set to 0", func() { - createConfigPolicyWithParent(policyYAML, policyName, configPolicyYAML) + createObjWithParent(policyYAML, policyName, configPolicyYAML, testNamespace, gvrPolicy, gvrConfigPolicy) }) It("checks the policy's history", func() { @@ -109,7 +110,7 @@ var _ = Describe("Test policy history messages when KubeAPI omits values in the ) It("sets up a configuration policy with an omitempty number set to 0", func() { - createConfigPolicyWithParent(policyYAML, policyName, configPolicyYAML) + createObjWithParent(policyYAML, policyName, configPolicyYAML, testNamespace, gvrPolicy, gvrConfigPolicy) }) It("checks the policy's history", func() { @@ -136,7 +137,7 @@ var _ = Describe("Test policy history messages when KubeAPI omits values in the ) It("sets up a configuration policy with struct set to null", func() { - createConfigPolicyWithParent(policyYAML, policyName, configPolicyYAML) + createObjWithParent(policyYAML, policyName, configPolicyYAML, testNamespace, gvrPolicy, gvrConfigPolicy) }) It("checks the policy's history", func() { @@ -221,27 +222,29 @@ var _ = Describe("Test policy history messages when KubeAPI omits values in the }) }) -func createConfigPolicyWithParent(parentPolicyYAML, parentPolicyName, configPolicyYAML string) { - By("Creating the parent policy") - utils.Kubectl("apply", "-f", parentPolicyYAML, "-n", testNamespace) - parent := utils.GetWithTimeout(clientManagedDynamic, gvrPolicy, - parentPolicyName, testNamespace, true, defaultTimeoutSeconds) +func createObjWithParent( + parentYAML, parentName, childYAML, namespace string, parentGVR, childGVR schema.GroupVersionResource, +) { + By("Creating the parent object") + utils.Kubectl("apply", "-f", parentYAML, "-n", namespace) + parent := utils.GetWithTimeout(clientManagedDynamic, parentGVR, + parentName, namespace, true, defaultTimeoutSeconds) Expect(parent).NotTo(BeNil()) - plcDef := utils.ParseYaml(configPolicyYAML) - ownerRefs := plcDef.GetOwnerReferences() + child := utils.ParseYaml(childYAML) + ownerRefs := child.GetOwnerReferences() ownerRefs[0].UID = parent.GetUID() - plcDef.SetOwnerReferences(ownerRefs) + child.SetOwnerReferences(ownerRefs) - By("Creating the configuration policy with the owner reference") + By("Creating the child object with the owner reference") - _, err := clientManagedDynamic.Resource(gvrConfigPolicy).Namespace(testNamespace). - Create(context.TODO(), plcDef, metav1.CreateOptions{}) + _, err := clientManagedDynamic.Resource(childGVR).Namespace(namespace). + Create(context.TODO(), child, metav1.CreateOptions{}) Expect(err).ToNot(HaveOccurred()) - By("Verifying the configuration policy exists") + By("Verifying the child object exists") - plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, - plcDef.GetName(), testNamespace, true, defaultTimeoutSeconds) + plc := utils.GetWithTimeout(clientManagedDynamic, childGVR, + child.GetName(), namespace, true, defaultTimeoutSeconds) Expect(plc).NotTo(BeNil()) } diff --git a/test/e2e/case34_enforce_w_status_test.go b/test/e2e/case34_enforce_w_status_test.go index 796e98bf..6afdbaa4 100644 --- a/test/e2e/case34_enforce_w_status_test.go +++ b/test/e2e/case34_enforce_w_status_test.go @@ -26,7 +26,7 @@ var _ = Describe("Test compliance events of enforced policies that define a stat It("Should have the expected events", func() { By("Setting up the policy") - createConfigPolicyWithParent(policyYAML, policyName, cfgPlcYAML) + createObjWithParent(policyYAML, policyName, cfgPlcYAML, testNamespace, gvrPolicy, gvrConfigPolicy) By("Checking there is a NonCompliant event on the policy") Eventually(func() interface{} { diff --git a/test/e2e/case35_no_apiversion_test.go b/test/e2e/case35_no_apiversion_test.go index 2cc4c917..e0aa2b61 100644 --- a/test/e2e/case35_no_apiversion_test.go +++ b/test/e2e/case35_no_apiversion_test.go @@ -21,7 +21,7 @@ var _ = Describe("Test a policy with an objectDefinition that is missing apiVers It("Should have the expected events", func() { By("Setting up the policy") - createConfigPolicyWithParent(policyYAML, policyName, cfgPlcYAML) + createObjWithParent(policyYAML, policyName, cfgPlcYAML, testNamespace, gvrPolicy, gvrConfigPolicy) By("Checking there is a NonCompliant event on the policy") Eventually(func() interface{} { diff --git a/test/e2e/case38_install_operator_test.go b/test/e2e/case38_install_operator_test.go index 32f8351b..10b53019 100644 --- a/test/e2e/case38_install_operator_test.go +++ b/test/e2e/case38_install_operator_test.go @@ -1,126 +1,518 @@ package e2e import ( + "encoding/json" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + policyv1 "open-cluster-management.io/config-policy-controller/api/v1" + policyv1beta1 "open-cluster-management.io/config-policy-controller/api/v1beta1" "open-cluster-management.io/config-policy-controller/test/utils" ) var _ = Describe("Test installing an operator from OperatorPolicy", Ordered, func() { const ( - case38BasePath = "../resources/case38_operator_install/" - case38OpPolicy = case38BasePath + "case38_OpPlc.yaml" - case38OpPolicyDefaultOg = case38BasePath + "case38_OpPlc_default_og.yaml" - case38OpPolicyName = "test-operator-policy" - case38OpPolicyDefaultOgName = "test-operator-policy-no-og" - case38SubscriptionName = "project-quay" - case38DeploymentName = "quay-operator-tng" - case38OpPolicyNS = "op-1" - case38OpPolicyDefaultOgNS = "op-2" - case38OgName = "og-single" - case38DefaultOgName = case38SubscriptionName + "-default-og" + opPolTestNS = "operator-policy-testns" + parentPolicyYAML = "../resources/case38_operator_install/parent-policy.yaml" + parentPolicyName = "parent-policy" + opPolTimeout = 10 ) - Describe("The operator is being installed for the first time", Ordered, func() { - Context("When there is a OperatorGroup spec specified", Ordered, func() { - BeforeAll(func() { - // Create ns op-1 - utils.Kubectl("create", "ns", case38OpPolicyNS) - // Apply a policy in ns op-1 - utils.Kubectl("apply", "-f", case38OpPolicy, "-n", case38OpPolicyNS) - // Check if applied properly - op := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorPolicy, case38OpPolicyName, - case38OpPolicyNS, true, defaultTimeoutSeconds) - Expect(op).NotTo(BeNil()) - }) + check := func( + polName string, + wantNonCompliant bool, + expectedRelatedObjs []policyv1.RelatedObject, + expectedCondition metav1.Condition, + expectedEventMsgSnippet string, + ) { + checkFunc := func(g Gomega) { + GinkgoHelper() - It("Should create the OperatorGroup", func() { - og := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorGroup, - case38OgName, case38OpPolicyNS, true, defaultTimeoutSeconds) + unstructPolicy := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorPolicy, polName, + opPolTestNS, true, opPolTimeout) - Expect(og).NotTo(BeNil()) - }) + policyJSON, err := json.MarshalIndent(unstructPolicy.Object, "", " ") + g.Expect(err).NotTo(HaveOccurred()) - It("Should create the Subscription from the spec", func() { - sub := utils.GetWithTimeout(clientManagedDynamic, gvrSubscription, - case38SubscriptionName, case38OpPolicyNS, true, defaultTimeoutSeconds) + GinkgoWriter.Printf("Debug info for failure.\npolicy JSON: %s\nwanted related objects: %+v\n"+ + "wanted condition: %+v\n", string(policyJSON), expectedRelatedObjs, expectedCondition) - Expect(sub).NotTo(BeNil()) - }) + policy := policyv1beta1.OperatorPolicy{} + err = json.Unmarshal(policyJSON, &policy) + g.Expect(err).NotTo(HaveOccurred()) - It("Should become Compliant", func() { - OpPlc := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorPolicy, - case38OpPolicyName, case38OpPolicyNS, true, defaultTimeoutSeconds) + if wantNonCompliant { + g.Expect(policy.Status.ComplianceState).To(Equal(policyv1.NonCompliant)) + } - Expect(utils.GetComplianceState(OpPlc)).To(Equal("Compliant")) - }) + matchingRelated := policy.Status.RelatedObjsOfKind(expectedRelatedObjs[0].Object.Kind) + g.Expect(matchingRelated).To(HaveLen(len(expectedRelatedObjs))) - It("Should have installed the operator", func() { - deployment := utils.GetWithTimeout(clientManagedDynamic, gvrDeployment, - case38DeploymentName, case38OpPolicyNS, true, defaultTimeoutSeconds*2) + for _, expectedRelObj := range expectedRelatedObjs { + foundMatchingName := false + unnamed := expectedRelObj.Object.Metadata.Name == "" - Expect(deployment).NotTo(BeNil()) - }) + for _, actualRelObj := range matchingRelated { + if unnamed || actualRelObj.Object.Metadata.Name == expectedRelObj.Object.Metadata.Name { + foundMatchingName = true + g.Expect(actualRelObj.Compliant).To(Equal(expectedRelObj.Compliant)) + g.Expect(actualRelObj.Reason).To(Equal(expectedRelObj.Reason)) + } + } + + g.Expect(foundMatchingName).To(BeTrue()) + } + + idx, actualCondition := policy.Status.GetCondition(expectedCondition.Type) + g.Expect(idx).NotTo(Equal(-1)) + g.Expect(actualCondition.Status).To(Equal(expectedCondition.Status)) + g.Expect(actualCondition.Reason).To(Equal(expectedCondition.Reason)) + g.Expect(actualCondition.Message).To(Equal(expectedCondition.Message)) + + g.Expect(utils.GetMatchingEvents( + clientManaged, opPolTestNS, parentPolicyName, "", expectedEventMsgSnippet, opPolTimeout, + )).NotTo(BeEmpty()) + } - AfterAll(func() { - // Delete related resources created during test - utils.Kubectl("delete", "operatorpolicy", case38OpPolicyName, "-n", case38OpPolicyNS) - utils.Kubectl("delete", "subscription", case38SubscriptionName, "-n", case38OpPolicyNS) - utils.Kubectl("delete", "operatorgroup", case38OgName, "-n", case38OpPolicyNS) - utils.Kubectl("delete", "ns", case38OpPolicyNS) + EventuallyWithOffset(1, checkFunc, opPolTimeout, 3).Should(Succeed()) + ConsistentlyWithOffset(1, checkFunc, 3, 1).Should(Succeed()) + } + + Describe("Testing OperatorGroup behavior when it is not specified in the policy", Ordered, func() { + const ( + opPolYAML = "../resources/case38_operator_install/operator-policy-no-group.yaml" + opPolName = "oppol-no-group" + extraOpGroupYAML = "../resources/case38_operator_install/extra-operator-group.yaml" + extraOpGroupName = "extra-operator-group" + ) + BeforeAll(func() { + utils.Kubectl("create", "ns", opPolTestNS) + DeferCleanup(func() { + utils.Kubectl("delete", "ns", opPolTestNS) }) + + createObjWithParent(parentPolicyYAML, parentPolicyName, + opPolYAML, opPolTestNS, gvrPolicy, gvrOperatorPolicy) + }) + + It("Should initially be NonCompliant", func() { + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: "*", + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource not found but should exist", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionFalse, + Reason: "OperatorGroupMissing", + Message: "the OperatorGroup required by the policy was not found", + }, + "the OperatorGroup required by the policy was not found", + ) }) + It("Should create the OperatorGroup when it is enforced", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/remediationAction", "value": "enforce"}]`) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionTrue, + Reason: "OperatorGroupMatches", + Message: "the OperatorGroup matches what is required by the policy", + }, + "the OperatorGroup required by the policy was created", + ) + }) + It("Should become NonCompliant when an extra OperatorGroup is added", func() { + utils.Kubectl("apply", "-f", extraOpGroupYAML, "-n", opPolTestNS) + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + }, + Compliant: "NonCompliant", + Reason: "There is more than one OperatorGroup in this namespace", + }, { + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: extraOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "There is more than one OperatorGroup in this namespace", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionFalse, + Reason: "TooManyOperatorGroups", + Message: "there is more than one OperatorGroup in the namespace", + }, + "there is more than one OperatorGroup in the namespace", + ) + }) + It("Should warn about the OperatorGroup when it doesn't match the default", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/remediationAction", "value": "inform"}]`) + utils.Kubectl("delete", "operatorgroup", "-n", opPolTestNS, "--all") + utils.Kubectl("apply", "-f", extraOpGroupYAML, "-n", opPolTestNS) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionTrue, + Reason: "PreexistingOperatorGroupFound", + Message: "the policy does not specify an OperatorGroup but one already exists in the namespace" + + " - assuming that OperatorGroup is correct", + }, + "assuming that OperatorGroup is correct", + ) + }) + }) + Describe("Testing OperatorGroup behavior when it is specified in the policy", Ordered, func() { + const ( + opPolYAML = "../resources/case38_operator_install/operator-policy-with-group.yaml" + opPolName = "oppol-with-group" + incorrectOpGroupYAML = "../resources/case38_operator_install/incorrect-operator-group.yaml" + incorrectOpGroupName = "incorrect-operator-group" + scopedOpGroupYAML = "../resources/case38_operator_install/scoped-operator-group.yaml" + scopedOpGroupName = "scoped-operator-group" + ) - Context("When there is no OperatorGroup spec specified", Ordered, func() { - BeforeAll(func() { - // Create ns op-2 - utils.Kubectl("create", "ns", case38OpPolicyDefaultOgNS) - // Apply a policy in ns op-2 - utils.Kubectl("apply", "-f", case38OpPolicyDefaultOg, "-n", case38OpPolicyDefaultOgNS) - // Check if applied properly - op := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorPolicy, case38OpPolicyDefaultOgName, - case38OpPolicyDefaultOgNS, true, defaultTimeoutSeconds) - Expect(op).NotTo(BeNil()) + BeforeAll(func() { + utils.Kubectl("create", "ns", opPolTestNS) + DeferCleanup(func() { + utils.Kubectl("delete", "ns", opPolTestNS) }) - It("Should create a default OperatorGroup", func() { - og := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorGroup, - case38DefaultOgName, case38OpPolicyDefaultOgNS, true, defaultTimeoutSeconds) + utils.Kubectl("apply", "-f", incorrectOpGroupYAML, "-n", opPolTestNS) - Expect(og).NotTo(BeNil()) - }) + createObjWithParent(parentPolicyYAML, parentPolicyName, + opPolYAML, opPolTestNS, gvrPolicy, gvrOperatorPolicy) + }) - It("Should create the Subscription from the spec", func() { - sub := utils.GetWithTimeout(clientManagedDynamic, gvrSubscription, - case38SubscriptionName, case38OpPolicyDefaultOgNS, true, defaultTimeoutSeconds) + It("Should initially be NonCompliant", func() { + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: incorrectOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource found but does not match", + }, { + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: scopedOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource not found but should exist", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionFalse, + Reason: "OperatorGroupMismatch", + Message: "the OperatorGroup found on the cluster does not match the policy", + }, + "the OperatorGroup found on the cluster does not match the policy", + ) + }) + It("Should match when the OperatorGroup is manually corrected", func() { + utils.Kubectl("delete", "operatorgroup", incorrectOpGroupName, "-n", opPolTestNS) + utils.Kubectl("apply", "-f", scopedOpGroupYAML, "-n", opPolTestNS) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: scopedOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionTrue, + Reason: "OperatorGroupMatches", + Message: "the OperatorGroup matches what is required by the policy", + }, + "the OperatorGroup matches what is required by the policy", + ) + }) + It("Should report a mismatch when the OperatorGroup is manually edited", func() { + utils.Kubectl("patch", "operatorgroup", scopedOpGroupName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/targetNamespaces", "value": []}]`) + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: scopedOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource found but does not match", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionFalse, + Reason: "OperatorGroupMismatch", + Message: "the OperatorGroup found on the cluster does not match the policy", + }, + "the OperatorGroup found on the cluster does not match the policy", + ) + }) + It("Should update the OperatorGroup when it is changed to enforce", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/remediationAction", "value": "enforce"}]`) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "OperatorGroup", + APIVersion: "operators.coreos.com/v1", + Metadata: policyv1.ObjectMetadata{ + Name: scopedOpGroupName, + Namespace: opPolTestNS, + }, + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "OperatorGroupCompliant", + Status: metav1.ConditionTrue, + Reason: "OperatorGroupMatches", + Message: "the OperatorGroup matches what is required by the policy", + }, + "the OperatorGroup was updated to match the policy", + ) + }) + }) + Describe("Testing Subscription behavior for musthave mode while enforcing", Ordered, func() { + const ( + opPolYAML = "../resources/case38_operator_install/operator-policy-no-group.yaml" + opPolName = "oppol-no-group" + subName = "project-quay" + ) - Expect(sub).NotTo(BeNil()) + BeforeAll(func() { + utils.Kubectl("create", "ns", opPolTestNS) + DeferCleanup(func() { + utils.Kubectl("delete", "ns", opPolTestNS) }) - It("Should become Compliant", func() { - Eventually(func() interface{} { - OpPlc := utils.GetWithTimeout(clientManagedDynamic, gvrOperatorPolicy, - case38OpPolicyDefaultOgName, case38OpPolicyDefaultOgNS, true, defaultTimeoutSeconds) + createObjWithParent(parentPolicyYAML, parentPolicyName, + opPolYAML, opPolTestNS, gvrPolicy, gvrOperatorPolicy) + }) - return utils.GetComplianceState(OpPlc) - }, defaultTimeoutSeconds, 1).Should(Equal("Compliant")) + It("Should initially be NonCompliant", func() { + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "Subscription", + APIVersion: "operators.coreos.com/v1alpha1", + Metadata: policyv1.ObjectMetadata{ + Name: subName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource not found but should exist", + }}, + metav1.Condition{ + Type: "SubscriptionCompliant", + Status: metav1.ConditionFalse, + Reason: "SubscriptionMissing", + Message: "the Subscription required by the policy was not found", + }, + "the Subscription required by the policy was not found", + ) + }) + It("Should create the Subscription when enforced", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/remediationAction", "value": "enforce"}]`) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "Subscription", + APIVersion: "operators.coreos.com/v1alpha1", + Metadata: policyv1.ObjectMetadata{ + Name: subName, + Namespace: opPolTestNS, + }, + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "SubscriptionCompliant", + Status: metav1.ConditionTrue, + Reason: "SubscriptionMatches", + Message: "the Subscription matches what is required by the policy", + }, + "the Subscription required by the policy was created", + ) + }) + It("Should apply an update to the Subscription", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/subscription/sourceNamespace", "value": "fake"}]`) + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "Subscription", + APIVersion: "operators.coreos.com/v1alpha1", + Metadata: policyv1.ObjectMetadata{ + Name: subName, + Namespace: opPolTestNS, + }, + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "SubscriptionCompliant", + Status: metav1.ConditionTrue, + Reason: "SubscriptionMatches", + Message: "the Subscription matches what is required by the policy", + }, + "the Subscription was updated to match the policy", + ) + }) + }) + Describe("Testing Subscription behavior for musthave mode while informing", Ordered, func() { + const ( + opPolYAML = "../resources/case38_operator_install/operator-policy-no-group.yaml" + opPolName = "oppol-no-group" + subName = "project-quay" + subYAML = "../resources/case38_operator_install/subscription.yaml" + ) + + BeforeAll(func() { + utils.Kubectl("create", "ns", opPolTestNS) + DeferCleanup(func() { + utils.Kubectl("delete", "ns", opPolTestNS) }) - It("Should have installed the operator", func() { - deployment := utils.GetWithTimeout(clientManagedDynamic, gvrDeployment, - case38DeploymentName, case38OpPolicyDefaultOgNS, true, defaultTimeoutSeconds*2) + utils.Kubectl("apply", "-f", subYAML, "-n", opPolTestNS) - Expect(deployment).NotTo(BeNil()) - }) + createObjWithParent(parentPolicyYAML, parentPolicyName, + opPolYAML, opPolTestNS, gvrPolicy, gvrOperatorPolicy) + }) - AfterAll(func() { - // Delete related resources created during test - utils.Kubectl("delete", "operatorpolicy", case38OpPolicyDefaultOgName, "-n", case38OpPolicyDefaultOgNS) - utils.Kubectl("delete", "subscription", case38SubscriptionName, "-n", case38OpPolicyDefaultOgNS) - utils.Kubectl("delete", "operatorgroup", case38DefaultOgName, "-n", case38OpPolicyDefaultOgNS) - utils.Kubectl("delete", "ns", case38OpPolicyDefaultOgNS) - }) + It("Should initially notice the matching Subscription", func() { + check( + opPolName, + false, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "Subscription", + APIVersion: "operators.coreos.com/v1alpha1", + Metadata: policyv1.ObjectMetadata{ + Name: subName, + Namespace: opPolTestNS, + }, + }, + Compliant: "Compliant", + Reason: "Resource found as expected", + }}, + metav1.Condition{ + Type: "SubscriptionCompliant", + Status: metav1.ConditionTrue, + Reason: "SubscriptionMatches", + Message: "the Subscription matches what is required by the policy", + }, + "the Subscription matches what is required by the policy", + ) + }) + It("Should notice the mismatch when the spec is changed in the policy", func() { + utils.Kubectl("patch", "operatorpolicy", opPolName, "-n", opPolTestNS, "--type=json", "-p", + `[{"op": "replace", "path": "/spec/subscription/sourceNamespace", "value": "fake"}]`) + check( + opPolName, + true, + []policyv1.RelatedObject{{ + Object: policyv1.ObjectResource{ + Kind: "Subscription", + APIVersion: "operators.coreos.com/v1alpha1", + Metadata: policyv1.ObjectMetadata{ + Name: subName, + Namespace: opPolTestNS, + }, + }, + Compliant: "NonCompliant", + Reason: "Resource found but does not match", + }}, + metav1.Condition{ + Type: "SubscriptionCompliant", + Status: metav1.ConditionFalse, + Reason: "SubscriptionMismatch", + Message: "the Subscription found on the cluster does not match the policy", + }, + "the Subscription found on the cluster does not match the policy", + ) }) }) }) diff --git a/test/resources/case38_operator_install/extra-operator-group.yaml b/test/resources/case38_operator_install/extra-operator-group.yaml new file mode 100644 index 00000000..d069efb7 --- /dev/null +++ b/test/resources/case38_operator_install/extra-operator-group.yaml @@ -0,0 +1,6 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: extra-operator-group +spec: + targetNamespaces: [] diff --git a/test/resources/case38_operator_install/incorrect-operator-group.yaml b/test/resources/case38_operator_install/incorrect-operator-group.yaml new file mode 100644 index 00000000..7ead7176 --- /dev/null +++ b/test/resources/case38_operator_install/incorrect-operator-group.yaml @@ -0,0 +1,8 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: incorrect-operator-group +spec: + targetNamespaces: + - "the-limit-does-not-exist" + - "bad-namespace" diff --git a/test/resources/case38_operator_install/operator-policy-no-group.yaml b/test/resources/case38_operator_install/operator-policy-no-group.yaml new file mode 100644 index 00000000..86ceaa44 --- /dev/null +++ b/test/resources/case38_operator_install/operator-policy-no-group.yaml @@ -0,0 +1,21 @@ +apiVersion: policy.open-cluster-management.io/v1beta1 +kind: OperatorPolicy +metadata: + name: oppol-no-group + ownerReferences: + - apiVersion: policy.open-cluster-management.io/v1 + kind: Policy + name: parent-policy + uid: 12345678-90ab-cdef-1234-567890abcdef # must be replaced before creation +spec: + remediationAction: inform + severity: medium + complianceType: musthave + subscription: + channel: stable-3.8 + name: project-quay + namespace: operator-policy-testns + installPlanApproval: Automatic + source: operatorhubio-catalog + sourceNamespace: olm + startingCSV: quay-operator.v3.8.1 diff --git a/test/resources/case38_operator_install/operator-policy-with-group.yaml b/test/resources/case38_operator_install/operator-policy-with-group.yaml new file mode 100644 index 00000000..4e24d7f7 --- /dev/null +++ b/test/resources/case38_operator_install/operator-policy-with-group.yaml @@ -0,0 +1,26 @@ +apiVersion: policy.open-cluster-management.io/v1beta1 +kind: OperatorPolicy +metadata: + name: oppol-with-group + ownerReferences: + - apiVersion: policy.open-cluster-management.io/v1 + kind: Policy + name: parent-policy + uid: 12345678-90ab-cdef-1234-567890abcdef # must be replaced before creation +spec: + remediationAction: inform + severity: medium + complianceType: musthave + operatorGroup: # optional + name: scoped-operator-group + namespace: operator-policy-testns + targetNamespaces: + - operator-policy-testns + subscription: + channel: stable-3.8 + name: project-quay + namespace: operator-policy-testns + installPlanApproval: Automatic + source: operatorhubio-catalog + sourceNamespace: olm + startingCSV: quay-operator.v3.8.1 diff --git a/test/resources/case38_operator_install/parent-policy.yaml b/test/resources/case38_operator_install/parent-policy.yaml new file mode 100644 index 00000000..993675de --- /dev/null +++ b/test/resources/case38_operator_install/parent-policy.yaml @@ -0,0 +1,8 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: parent-policy +spec: + remediationAction: inform + disabled: false + policy-templates: [] diff --git a/test/resources/case38_operator_install/scoped-operator-group.yaml b/test/resources/case38_operator_install/scoped-operator-group.yaml new file mode 100644 index 00000000..7249ee27 --- /dev/null +++ b/test/resources/case38_operator_install/scoped-operator-group.yaml @@ -0,0 +1,7 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: scoped-operator-group +spec: + targetNamespaces: + - "operator-policy-testns" diff --git a/test/resources/case38_operator_install/subscription.yaml b/test/resources/case38_operator_install/subscription.yaml new file mode 100644 index 00000000..c4592a1d --- /dev/null +++ b/test/resources/case38_operator_install/subscription.yaml @@ -0,0 +1,11 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: project-quay +spec: + channel: stable-3.8 + name: project-quay + installPlanApproval: Automatic + source: operatorhubio-catalog + sourceNamespace: olm + startingCSV: quay-operator.v3.8.1