Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 90 additions & 11 deletions controllers/cli_download_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import (
)

const (
cliServerDeploymentName = "openshift-adp-oadp-cli-server"
cliServerServiceName = "openshift-adp-cli-server"
cliServerRouteName = "oadp-cli-server-route"
cliDownloadName = "openshift-adp-oadp-cli"
managedByLabel = "app.kubernetes.io/managed-by"
operatorName = "oadp-operator"
cliServerDeploymentName = "openshift-adp-oadp-cli-server"
cliServerServiceName = "openshift-adp-cli-server"
cliServerServiceAccountName = "openshift-adp-cli-server"
cliServerRouteName = "oadp-cli-server-route"
cliDownloadName = "openshift-adp-oadp-cli"
managedByLabel = "app.kubernetes.io/managed-by"
operatorName = "oadp-operator"
)

// CLIDownloadSetup is a runnable that sets up CLI download resources when the operator starts
Expand Down Expand Up @@ -74,9 +75,29 @@ func (c *CLIDownloadSetup) Start(ctx context.Context) error {
// reconcileCLIResources creates or updates all CLI-related resources.
// This is idempotent - it will reuse existing resources if they already exist.
func (c *CLIDownloadSetup) reconcileCLIResources(ctx context.Context, operatorDeployment *appsv1.Deployment, cliServerImage string) error {
// 1. Create or update the deployment
deployment := &appsv1.Deployment{}
// 1. Create the dedicated ServiceAccount used by the CLI server pod.
serviceAccount := &corev1.ServiceAccount{}
err := c.Client.Get(ctx, client.ObjectKey{
Name: cliServerServiceAccountName,
Namespace: c.Namespace,
}, serviceAccount)

if errors.IsNotFound(err) {
serviceAccount = buildCLIServerServiceAccount(c.Namespace)
if err := controllerutil.SetOwnerReference(operatorDeployment, serviceAccount, c.Client.Scheme()); err != nil {
return fmt.Errorf("failed to set owner reference on service account: %w", err)
}
if err := c.Client.Create(ctx, serviceAccount); err != nil && !errors.IsAlreadyExists(err) {
return fmt.Errorf("failed to create CLI server service account: %w", err)
}
c.Log.Info("Created CLI server service account")
} else if err != nil {
return fmt.Errorf("failed to get CLI server service account: %w", err)
}

// 2. Create or update the deployment
deployment := &appsv1.Deployment{}
err = c.Client.Get(ctx, client.ObjectKey{
Name: cliServerDeploymentName,
Namespace: c.Namespace,
}, deployment)
Expand All @@ -93,9 +114,29 @@ func (c *CLIDownloadSetup) reconcileCLIResources(ctx context.Context, operatorDe
c.Log.Info("Created CLI server deployment", "image", cliServerImage)
} else if err != nil {
return fmt.Errorf("failed to get CLI server deployment: %w", err)
} else {
desired := buildCLIServerDeployment(c.Namespace, cliServerImage)
needsUpdate := false
if len(deployment.Spec.Template.Spec.Containers) > 0 &&
deployment.Spec.Template.Spec.Containers[0].ReadinessProbe == nil {
deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = desired.Spec.Template.Spec.Containers[0].ReadinessProbe
deployment.Spec.Template.Spec.Containers[0].LivenessProbe = desired.Spec.Template.Spec.Containers[0].LivenessProbe
needsUpdate = true
}
if deployment.Spec.Template.Spec.ServiceAccountName != cliServerServiceAccountName {
deployment.Spec.Template.Spec.ServiceAccountName = desired.Spec.Template.Spec.ServiceAccountName
deployment.Spec.Template.Spec.AutomountServiceAccountToken = desired.Spec.Template.Spec.AutomountServiceAccountToken
needsUpdate = true
}
if needsUpdate {
if err := c.Client.Update(ctx, deployment); err != nil {
return fmt.Errorf("failed to update CLI server deployment: %w", err)
}
c.Log.Info("Updated CLI server deployment with readiness/liveness probes and/or service account")
}
}

// 2. Create or update the service
// 3. Create or update the service
service := &corev1.Service{}
err = c.Client.Get(ctx, client.ObjectKey{
Name: cliServerServiceName,
Expand All @@ -116,7 +157,7 @@ func (c *CLIDownloadSetup) reconcileCLIResources(ctx context.Context, operatorDe
return fmt.Errorf("failed to get CLI server service: %w", err)
}

// 3. Create or get the route
// 4. Create or get the route
route := &routev1.Route{}
err = c.Client.Get(ctx, client.ObjectKey{
Name: cliServerRouteName,
Expand Down Expand Up @@ -181,7 +222,7 @@ func (c *CLIDownloadSetup) reconcileCLIResources(ctx context.Context, operatorDe
}
}

// 4. Create or update ConsoleCLIDownload (cluster-scoped)
// 5. Create or update ConsoleCLIDownload (cluster-scoped)
downloadURL := fmt.Sprintf("https://%s/", hostname)

consoleCLIDownload := &consolev1.ConsoleCLIDownload{}
Expand Down Expand Up @@ -252,6 +293,7 @@ func buildCLIServerDeployment(namespace, image string) *appsv1.Deployment {
runAsNonRoot := true
allowPrivilegeEscalation := false
readOnlyRootFilesystem := true
automountServiceAccountToken := false

return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -276,6 +318,8 @@ func buildCLIServerDeployment(namespace, image string) *appsv1.Deployment {
},
},
Spec: corev1.PodSpec{
ServiceAccountName: cliServerServiceAccountName,
AutomountServiceAccountToken: &automountServiceAccountToken,
SecurityContext: &corev1.PodSecurityContext{
RunAsNonRoot: &runAsNonRoot,
},
Expand All @@ -290,6 +334,26 @@ func buildCLIServerDeployment(namespace, image string) *appsv1.Deployment {
Protocol: corev1.ProtocolTCP,
},
},
ReadinessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/",
Port: intstr.FromString("http"),
},
},
InitialDelaySeconds: 5,
PeriodSeconds: 10,
},
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/",
Port: intstr.FromString("http"),
},
},
InitialDelaySeconds: 15,
PeriodSeconds: 20,
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
Expand All @@ -316,6 +380,21 @@ func buildCLIServerDeployment(namespace, image string) *appsv1.Deployment {
}
}

func buildCLIServerServiceAccount(namespace string) *corev1.ServiceAccount {
automountServiceAccountToken := false
return &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: cliServerServiceAccountName,
Namespace: namespace,
Labels: map[string]string{
"app": "oadp-cli",
managedByLabel: operatorName,
},
},
AutomountServiceAccountToken: &automountServiceAccountToken,
}
}

func buildCLIServerService(namespace string) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Loading