diff --git a/README.md b/README.md index fd80218..ba57ea9 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,13 @@ spec: args: ["nginx", "-g", "daemon off;"] command: ["nginx"] rolloutStrategy: rollingUpdate # or "recreate" + nodeSelector: + kubernetes.io/hostname: minikube + tolerations: + - key: "storage" + operator: "Equal" + value: "ssd" + effect: "NoSchedule" image: registry: "docker.io" repository: "nginx" diff --git a/api/v1alpha1/rollout_types.go b/api/v1alpha1/rollout_types.go index 60cdf9c..94751f8 100644 --- a/api/v1alpha1/rollout_types.go +++ b/api/v1alpha1/rollout_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -127,6 +128,8 @@ type RolloutSpec struct { Interfaces []InterfaceSpec `json:"interfaces,omitempty"` ServiceAccountName string `json:"serviceAccountName"` CronJobs []CronJobSpec `json:"cronjobs,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` } type Resources struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 0303257..ba59456 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -395,6 +396,20 @@ func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. diff --git a/config/crd/bases/one-click.dev_rollouts.yaml b/config/crd/bases/one-click.dev_rollouts.yaml index 1ffcd8a..a6e9a3a 100644 --- a/config/crd/bases/one-click.dev_rollouts.yaml +++ b/config/crd/bases/one-click.dev_rollouts.yaml @@ -249,6 +249,10 @@ spec: - port type: object type: array + nodeSelector: + additionalProperties: + type: string + type: object resources: properties: limits: @@ -322,6 +326,45 @@ spec: type: object serviceAccountName: type: string + 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 volumes: items: properties: diff --git a/controllers/deployment.go b/controllers/deployment.go index 393a98f..dd75e45 100644 --- a/controllers/deployment.go +++ b/controllers/deployment.go @@ -116,6 +116,8 @@ func (r *RolloutReconciler) deploymentForRollout(ctx context.Context, f *oneclic }}, ServiceAccountName: f.Spec.ServiceAccountName, ImagePullSecrets: imagePullSecrets, + NodeSelector: f.Spec.NodeSelector, + Tolerations: getTolerations(f.Spec.Tolerations), }, }, Strategy: strategy, @@ -313,11 +315,35 @@ func needsUpdate(current *appsv1.Deployment, f *oneclickiov1alpha1.Rollout) bool return true } + // Check node selector + if !reflect.DeepEqual(current.Spec.Template.Spec.NodeSelector, f.Spec.NodeSelector) { + return true + } + + // Check tolerations + if !reflect.DeepEqual(current.Spec.Template.Spec.Tolerations, getTolerations(f.Spec.Tolerations)) { + return true + } + // Add more checks as necessary, e.g., labels, annotations, specific configuration, etc. return false } +func getTolerations(tolerations []corev1.Toleration) []corev1.Toleration { + var result []corev1.Toleration + for _, t := range tolerations { + result = append(result, corev1.Toleration{ + Key: t.Key, + Operator: corev1.TolerationOperator(t.Operator), + Value: t.Value, + Effect: corev1.TaintEffect(t.Effect), + TolerationSeconds: t.TolerationSeconds, + }) + } + return result +} + func volumesMatch(currentVolumes []corev1.Volume, desiredVolumes []oneclickiov1alpha1.VolumeSpec, f *oneclickiov1alpha1.Rollout) bool { if len(currentVolumes) != len(desiredVolumes) { return false