diff --git a/api/v1/nodefeaturediscovery_types.go b/api/v1/nodefeaturediscovery_types.go index 70716853..37d571a2 100644 --- a/api/v1/nodefeaturediscovery_types.go +++ b/api/v1/nodefeaturediscovery_types.go @@ -58,11 +58,6 @@ type NodeFeatureDiscoverySpec struct { // OperandSpec describes configuration options for the operand type OperandSpec struct { - // Namespace defines the namespace to deploy nfd-master - // and nfd-worker pods - // +kubebuilder:validation:Pattern=[a-zA-Z0-9\.\-\/]+ - Namespace string `json:"namespace,omitempty"` - // Image defines the image to pull for the // NFD operand // [defaults to k8s.gcr.io/nfd/node-feature-discovery] diff --git a/config/crd/bases/nfd.kubernetes.io_nodefeaturediscoveries.yaml b/config/crd/bases/nfd.kubernetes.io_nodefeaturediscoveries.yaml index bb7a2412..79d2b56d 100644 --- a/config/crd/bases/nfd.kubernetes.io_nodefeaturediscoveries.yaml +++ b/config/crd/bases/nfd.kubernetes.io_nodefeaturediscoveries.yaml @@ -67,11 +67,6 @@ spec: description: ImagePullPolicy defines Image pull policy for the NFD operand image [defaults to Always] type: string - namespace: - description: Namespace defines the namespace to deploy nfd-master - and nfd-worker pods - pattern: '[a-zA-Z0-9\.\-\/]+' - type: string servicePort: description: ServicePort specifies the TCP port that nfd-master listens for incoming requests. @@ -105,26 +100,69 @@ spec: description: Conditions represents the latest available observations of current state. items: - description: Condition represents the state of the operator's reconciliation - functionality. + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: - lastHeartbeatTime: - format: date-time - type: string lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown type: string type: - description: ConditionType is the state of the operator's reconciliation - functionality. + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: + - lastTransitionTime + - message + - reason - status - type type: object diff --git a/config/samples/nfd.kubernetes.io_v1_nodefeaturediscovery.yaml b/config/samples/nfd.kubernetes.io_v1_nodefeaturediscovery.yaml index a27c16ed..4a93614f 100644 --- a/config/samples/nfd.kubernetes.io_v1_nodefeaturediscovery.yaml +++ b/config/samples/nfd.kubernetes.io_v1_nodefeaturediscovery.yaml @@ -11,7 +11,6 @@ spec: #resourceLabels: # - "example.com/resource" operand: - namespace: node-feature-discovery-operator image: gcr.io/k8s-staging-nfd/node-feature-discovery:master imagePullPolicy: Always servicePort: 12000 diff --git a/controllers/nodefeaturediscovery_controller.go b/controllers/nodefeaturediscovery_controller.go index d1917dd5..c8d04909 100644 --- a/controllers/nodefeaturediscovery_controller.go +++ b/controllers/nodefeaturediscovery_controller.go @@ -157,49 +157,49 @@ func (r *NodeFeatureDiscoveryReconciler) Reconcile(ctx context.Context, req ctrl } // Check the status of the NFD Operator Worker ServiceAccount - if rstatus, err := r.getWorkerServiceAccountConditions(ctx); err != nil { + if rstatus, err := r.getWorkerServiceAccountConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDWorkerServiceAccount, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDWorkerServiceAccountDegraded, "nfd-worker service account has been degraded") } // Check the status of the NFD Operator Master ServiceAccount - if rstatus, err := r.getMasterServiceAccountConditions(ctx); err != nil { + if rstatus, err := r.getMasterServiceAccountConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDMasterServiceAccount, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDMasterServiceAccountDegraded, "nfd-master service account has been degraded") } // Check the status of the NFD Operator role - if rstatus, err := r.getRoleConditions(ctx); err != nil { + if rstatus, err := r.getRoleConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionNFDRoleDegraded, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDRoleDegraded, "nfd-worker role has been degraded") } // Check the status of the NFD Operator cluster role - if rstatus, err := r.getClusterRoleConditions(ctx); err != nil { + if rstatus, err := r.getClusterRoleConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionNFDClusterRoleDegraded, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDClusterRoleDegraded, "nfd ClusterRole has been degraded") } // Check the status of the NFD Operator cluster role binding - if rstatus, err := r.getClusterRoleBindingConditions(ctx); err != nil { + if rstatus, err := r.getClusterRoleBindingConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDClusterRoleBinding, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDClusterRoleBindingDegraded, "nfd ClusterRoleBinding has been degraded") } // Check the status of the NFD Operator role binding - if rstatus, err := r.getRoleBindingConditions(ctx); err != nil { + if rstatus, err := r.getRoleBindingConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDRoleBinding, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDRoleBindingDegraded, "nfd RoleBinding has been degraded") } // Check the status of the NFD Operator Service - if rstatus, err := r.getServiceConditions(ctx); err != nil { + if rstatus, err := r.getServiceConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDService, err.Error()) } else if rstatus.isDegraded { return r.updateDegradedCondition(instance, conditionNFDServiceDegraded, "nfd Service has been degraded") @@ -213,7 +213,7 @@ func (r *NodeFeatureDiscoveryReconciler) Reconcile(ctx context.Context, req ctrl } // Check the status of the NFD Operator Worker DaemonSet - if rstatus, err := r.getWorkerDaemonSetConditions(ctx); err != nil { + if rstatus, err := r.getWorkerDaemonSetConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDWorkerDaemonSet, err.Error()) } else if rstatus.isProgressing { return r.updateProgressingCondition(instance, err.Error(), "nfd-worker Daemonset is progressing") @@ -222,7 +222,7 @@ func (r *NodeFeatureDiscoveryReconciler) Reconcile(ctx context.Context, req ctrl } // Check the status of the NFD Operator Master DaemonSet - if rstatus, err := r.getMasterDaemonSetConditions(ctx); err != nil { + if rstatus, err := r.getMasterDaemonSetConditions(ctx, instance); err != nil { return r.updateDegradedCondition(instance, conditionFailedGettingNFDMasterDaemonSet, err.Error()) } else if rstatus.isProgressing { return r.updateProgressingCondition(instance, err.Error(), "nfd-master Daemonset is progressing") diff --git a/controllers/nodefeaturediscovery_controls.go b/controllers/nodefeaturediscovery_controls.go index 38e36ca6..48d0a7fb 100644 --- a/controllers/nodefeaturediscovery_controls.go +++ b/controllers/nodefeaturediscovery_controls.go @@ -57,42 +57,6 @@ func (s ResourceStatus) String() string { return names[s] } -// Namespace checks if the Namespace for NFD exists and creates it -// if it doesn't exist -func Namespace(n NFD) (ResourceStatus, error) { - - // state represents the resource's 'control' function index - state := n.idx - - // It is assumed that the index has already been verified to be a - // Namespace object, so let's get the resource's Namespace object - obj := n.resources[state].Namespace - - // found states if the Namespace was found - found := &corev1.Namespace{} - - // Look for the Namespace to see if it exists, and if so, check if - // it's Ready/NotReady. If the Namespace does not exist, then - // attempt to create it - klog.Info("Looking for Namespace %q", obj.Name) - err := n.rec.Client.Get(context.TODO(), types.NamespacedName{Namespace: obj.Namespace, Name: obj.Name}, found) - if err != nil && errors.IsNotFound(err) { - klog.Info("Namespace %q Not found, creating", obj.Name) - err = n.rec.Client.Create(context.TODO(), &obj) - if err != nil { - klog.Info("Couldn't create Namespace %q", obj.Name) - return NotReady, err - } - return Ready, nil - } else if err != nil { - return NotReady, err - } - - klog.Info("Found Namespace %q, skipping update", obj.Name) - - return Ready, nil -} - // ServiceAccount checks the readiness of the NFD ServiceAccount and creates it if it doesn't exist func ServiceAccount(n NFD) (ResourceStatus, error) { diff --git a/controllers/nodefeaturediscovery_resources.go b/controllers/nodefeaturediscovery_resources.go index 15062466..a1c5672b 100644 --- a/controllers/nodefeaturediscovery_resources.go +++ b/controllers/nodefeaturediscovery_resources.go @@ -113,10 +113,6 @@ func addResourcesControls(path string) (Resources, controlFunc) { kind = strings.TrimSpace(slce[1]) switch kind { - case "Namespace": - _, _, err := s.Decode(m, nil, &res.Namespace) - panicIfError(err) - ctrl = append(ctrl, Namespace) case "ServiceAccount": _, _, err := s.Decode(m, nil, &res.ServiceAccount) panicIfError(err) @@ -153,7 +149,6 @@ func addResourcesControls(path string) (Resources, controlFunc) { default: klog.Info("Unknown Resource: ", "Kind", kind) } - } return res, ctrl diff --git a/controllers/nodefeaturediscovery_status.go b/controllers/nodefeaturediscovery_status.go index 8cdc3127..1054d544 100644 --- a/controllers/nodefeaturediscovery_status.go +++ b/controllers/nodefeaturediscovery_status.go @@ -30,7 +30,6 @@ import ( ) const ( - nfdNamespace = "node-feature-discovery-operator" nfdWorkerApp string = "nfd-worker" nfdMasterApp string = "nfd-master" ) @@ -267,23 +266,23 @@ func initializeDegradedStatus() Status { // getWorkerDaemonSetConditions is a wrapper around "getDaemonSetConditions" for // worker DaemonSets -func (r *NodeFeatureDiscoveryReconciler) getWorkerDaemonSetConditions(ctx context.Context) (Status, error) { - return r.getDaemonSetConditions(ctx, nfdWorkerApp) +func (r *NodeFeatureDiscoveryReconciler) getWorkerDaemonSetConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { + return r.getDaemonSetConditions(ctx, instance, nfdWorkerApp) } // getMasterDaemonSetConditions is a wrapper around "getDaemonSetConditions" for // master DaemonSets -func (r *NodeFeatureDiscoveryReconciler) getMasterDaemonSetConditions(ctx context.Context) (Status, error) { - return r.getDaemonSetConditions(ctx, nfdMasterApp) +func (r *NodeFeatureDiscoveryReconciler) getMasterDaemonSetConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { + return r.getDaemonSetConditions(ctx, instance, nfdMasterApp) } // getDaemonSetConditions gets the current status of a DaemonSet. If an error // occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getDaemonSetConditions(ctx context.Context, nfdAppName string) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getDaemonSetConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery, nfdAppName string) (Status, error) { // Initialize the resource's status to 'Degraded' status := initializeDegradedStatus() - ds, err := r.getDaemonSet(ctx, nfdNamespace, nfdAppName) + ds, err := r.getDaemonSet(ctx, instance.ObjectMeta.Namespace, nfdAppName) if err != nil { return status, err } @@ -350,12 +349,12 @@ func (r *NodeFeatureDiscoveryReconciler) getDaemonSetConditions(ctx context.Cont // getServiceConditions gets the current status of a Service. If an error // occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getServiceConditions(ctx context.Context) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getServiceConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the existing Service from the reconciler - _, err := r.getService(ctx, nfdNamespace, nfdMasterApp) + _, err := r.getService(ctx, instance.ObjectMeta.Namespace, nfdMasterApp) // If the Service could not be obtained, then it is degraded if err != nil { @@ -393,12 +392,12 @@ func (r *NodeFeatureDiscoveryReconciler) getWorkerConfigConditions(n NFD) (Statu // getRoleConditions gets the current status of a Role. If an error occurs, this // function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getRoleConditions(ctx context.Context) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getRoleConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the existing Role from the reconciler - _, err := r.getRole(ctx, nfdNamespace, nfdWorkerApp) + _, err := r.getRole(ctx, instance.ObjectMeta.Namespace, nfdWorkerApp) // If the error is not nil, then the Role hasn't been (re)created yet if err != nil { @@ -414,12 +413,12 @@ func (r *NodeFeatureDiscoveryReconciler) getRoleConditions(ctx context.Context) // getRoleBindingConditions gets the current status of a RoleBinding. If an error // occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getRoleBindingConditions(ctx context.Context) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getRoleBindingConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the existing RoleBinding from the reconciler - _, err := r.getRoleBinding(ctx, nfdNamespace, nfdWorkerApp) + _, err := r.getRoleBinding(ctx, instance.ObjectMeta.Namespace, nfdWorkerApp) // If the error is not nil, then the RoleBinding hasn't been (re)created yet if err != nil { @@ -435,12 +434,12 @@ func (r *NodeFeatureDiscoveryReconciler) getRoleBindingConditions(ctx context.Co // geClusterRoleConditions gets the current status of a ClusterRole. If an error // occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getClusterRoleConditions(ctx context.Context) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getClusterRoleConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the existing ClusterRole from the reconciler - _, err := r.getClusterRole(ctx, "", nfdMasterApp) + _, err := r.getClusterRole(ctx, instance.ObjectMeta.Namespace, nfdMasterApp) // If 'clusterRole' is nil, then it hasn't been (re)created yet if err != nil { @@ -456,12 +455,12 @@ func (r *NodeFeatureDiscoveryReconciler) getClusterRoleConditions(ctx context.Co // getClusterRoleBindingConditions gets the current status of a ClusterRoleBinding. // If an error occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getClusterRoleBindingConditions(ctx context.Context) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getClusterRoleBindingConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the existing ClusterRoleBinding from the reconciler - _, err := r.getClusterRoleBinding(ctx, "", nfdMasterApp) + _, err := r.getClusterRoleBinding(ctx, instance.ObjectMeta.Namespace, nfdMasterApp) // If the error is not nil, then the ClusterRoleBinding hasn't been (re)created // yet @@ -478,24 +477,24 @@ func (r *NodeFeatureDiscoveryReconciler) getClusterRoleBindingConditions(ctx con // getWorkerServiceAccountConditions is a wrapper around "getServiceAccountConditions" for // worker service account. -func (r *NodeFeatureDiscoveryReconciler) getWorkerServiceAccountConditions(ctx context.Context) (Status, error) { - return r.getServiceAccountConditions(ctx, nfdWorkerApp) +func (r *NodeFeatureDiscoveryReconciler) getWorkerServiceAccountConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { + return r.getServiceAccountConditions(ctx, instance, nfdWorkerApp) } // getMasterServiceAccountConditions is a wrapper around "getServiceAccountConditions" for // master service account. -func (r *NodeFeatureDiscoveryReconciler) getMasterServiceAccountConditions(ctx context.Context) (Status, error) { - return r.getServiceAccountConditions(ctx, nfdMasterApp) +func (r *NodeFeatureDiscoveryReconciler) getMasterServiceAccountConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery) (Status, error) { + return r.getServiceAccountConditions(ctx, instance, nfdMasterApp) } // getServiceAccountConditions gets the current status of a ServiceAccount. If an error // occurs, this function returns the corresponding error message -func (r *NodeFeatureDiscoveryReconciler) getServiceAccountConditions(ctx context.Context, nfdAppName string) (Status, error) { +func (r *NodeFeatureDiscoveryReconciler) getServiceAccountConditions(ctx context.Context, instance *nfdv1.NodeFeatureDiscovery, nfdAppName string) (Status, error) { // Initialize status to 'Degraded' status := initializeDegradedStatus() // Get the service account from the reconciler - _, err := r.getServiceAccount(ctx, nfdNamespace, nfdAppName) + _, err := r.getServiceAccount(ctx, instance.ObjectMeta.Namespace, nfdAppName) // If the error is not nil, then the ServiceAccount hasn't been (re)created yet if err != nil { diff --git a/docs/get-started/quick-start.md b/docs/get-started/quick-start.md index bc6d62ac..191d0721 100644 --- a/docs/get-started/quick-start.md +++ b/docs/get-started/quick-start.md @@ -25,6 +25,12 @@ IMAGE_TAG={{ site.container_image }} make deploy ``` +By default the operator will watch `NodeFeatureDiscovery` objects +only in the namespace where the operator is deployed in. This is +specified by the `WATCH_NAMESPACE` env variable in the operator +deployment manifest. If unset the operator will watch ALL +namespaces. + Create a NodeFeatureDiscovery instance ```bash diff --git a/main.go b/main.go index d631ea5a..7a661fd3 100644 --- a/main.go +++ b/main.go @@ -33,6 +33,7 @@ import ( nfdkubernetesiov1 "github.com/kubernetes-sigs/node-feature-discovery-operator/api/v1" "github.com/kubernetes-sigs/node-feature-discovery-operator/controllers" + "github.com/kubernetes-sigs/node-feature-discovery-operator/pkg/utils" // +kubebuilder:scaffold:imports ) @@ -82,6 +83,12 @@ func main() { os.Exit(0) } + watchNamespace, envSet := utils.GetWatchNamespace() + if !envSet { + klog.Info("unable to get WatchNamespace, " + + "the manager will watch and manage resources in all namespaces") + } + // Create a new manager to manage the operator mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, @@ -90,6 +97,7 @@ func main() { HealthProbeBindAddress: args.probeAddr, LeaderElection: args.enableLeaderElection, LeaderElectionID: "39f5e5c3.nodefeaturediscoveries.nfd.kubernetes.io", + Namespace: watchNamespace, }) if err != nil { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 00000000..b23d341a --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,30 @@ +/* +Copyright 2022. The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package utils + +import ( + "os" +) + +// WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE +// which specifies the Namespace to watch. +// An empty value means the operator is running with cluster scope. +const WatchNamespaceEnvVar = "WATCH_NAMESPACE" + +// GetWatchNamespace returns the Namespace the operator should be watching for changes +func GetWatchNamespace() (string, bool) { + return os.LookupEnv(WatchNamespaceEnvVar) +}