Skip to content

Deploy the registration service using the new SSA client #1171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 3 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ var (
)

const (
memberClientTimeout = 3 * time.Second
memberClientTimeout = 3 * time.Second
hostOperatorFieldManager = "kubesaw-host-operator"
)

func init() {
Expand Down Expand Up @@ -293,6 +294,7 @@ func main() { // nolint:gocyclo
Client: mgr.GetClient(),
GetMembersFunc: commoncluster.GetMemberClusters,
Scheme: mgr.GetScheme(),
FieldManager: hostOperatorFieldManager,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ToolchainConfig")
os.Exit(1)
Expand Down
50 changes: 10 additions & 40 deletions controllers/toolchainconfig/toolchainconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
Expand All @@ -36,7 +35,6 @@

// SetupWithManager sets up the controller with the Manager.
func (r *Reconciler) SetupWithManager(mgr manager.Manager) error {

regServiceTemplate, err := registrationservice.GetDeploymentTemplate()
if err != nil {
return errs.Wrap(err, "unable to decode the registration service deployment")
Expand All @@ -58,6 +56,7 @@
GetMembersFunc cluster.GetMemberClustersFunc
Scheme *runtime.Scheme
RegServiceTemplate *templatev1.Template
FieldManager string
}

//+kubebuilder:rbac:groups=toolchain.dev.openshift.com,resources=toolchainconfigs,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -101,7 +100,7 @@
// Load the latest config and secrets into the cache
cfg, err := ForceLoadToolchainConfig(r.Client)
if err != nil {
return reconcile.Result{}, r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, err, "failed to load the latest configuration")
return reconcile.Result{}, r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, fmt.Errorf("failed to load the latest configuration: %w", err))
}

// Deploy registration service
Expand All @@ -128,34 +127,18 @@

func (r *Reconciler) ensureRegistrationService(ctx context.Context, toolchainConfig *toolchainv1alpha1.ToolchainConfig, vars templateVars) error {
// process template with variables taken from the RegistrationService CRD
cl := applycl.NewApplyClient(r.Client)
cl := applycl.NewSSAApplyClient(r.Client, r.FieldManager)
toolchainObjects, err := template.NewProcessor(r.Scheme).Process(r.RegServiceTemplate.DeepCopy(), vars)
if err != nil {
return r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, err, "failed to process registration service template")
return r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, fmt.Errorf("failed to process registration service template: %w", err))

Check warning on line 133 in controllers/toolchainconfig/toolchainconfig_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/toolchainconfig/toolchainconfig_controller.go#L133

Added line #L133 was not covered by tests
}

// create all objects that are within the template, and update only when the object has changed.
var updated []string
for _, toolchainObject := range toolchainObjects {
createdOrUpdated, err := cl.ApplyObject(ctx, toolchainObject, applycl.SetOwner(toolchainConfig))
if err != nil {
return r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, err, "failed to apply registration service object %s", toolchainObject.GetName())
}
if createdOrUpdated {
gvk, err := apiutil.GVKForObject(toolchainObject, r.Scheme)
if err != nil {
return r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, err, "failed to determine GroupVersionKind for registration service object %s", toolchainObject.GetName())
}
updated = append(updated, fmt.Sprintf("%s: %s", gvk.Kind, toolchainObject.GetName()))
}
if err := cl.Apply(ctx, toolchainObjects, applycl.SetOwnerReference(toolchainConfig)); err != nil {
return r.WrapErrorWithStatusUpdate(ctx, toolchainConfig, r.setStatusDeployRegistrationServiceFailed, fmt.Errorf("failed to apply registration service: %w", err))
}

logger := log.FromContext(ctx)
if len(updated) > 0 {
logger.Info("Updated registration service resources", "updated resources", updated)
return r.updateStatusCondition(ctx, toolchainConfig, ToRegServiceDeploying(fmt.Sprintf("updated resources: %s", updated)), false)
}

logger.Info("All objects in registration service template have been created and are up-to-date")
return r.updateStatusCondition(ctx, toolchainConfig, ToRegServiceDeployComplete(), false)
}
Expand Down Expand Up @@ -200,19 +183,16 @@
ctx context.Context,
toolchainConfig *toolchainv1alpha1.ToolchainConfig,
statusUpdater func(ctx context.Context, toolchainConfig *toolchainv1alpha1.ToolchainConfig, message string) error,
providedError error,
format string,
args ...interface{},
err error,
) error {
if providedError == nil {
if err == nil {
return nil
}
wrappedErr := errs.Wrapf(providedError, format, args...)
if err := statusUpdater(ctx, toolchainConfig, wrappedErr.Error()); err != nil {
if err := statusUpdater(ctx, toolchainConfig, err.Error()); err != nil {
logger := log.FromContext(ctx)
logger.Error(err, "status update failed")
}
return wrappedErr
return err
}

// ToSyncComplete condition when the sync completed with success
Expand Down Expand Up @@ -243,16 +223,6 @@
}
}

// ToRegServiceDeploying condition when deploying is in progress
func ToRegServiceDeploying(msg string) toolchainv1alpha1.Condition {
return toolchainv1alpha1.Condition{
Type: toolchainv1alpha1.ToolchainConfigRegServiceDeploy,
Status: corev1.ConditionFalse,
Reason: toolchainv1alpha1.ToolchainConfigRegServiceDeployingReason,
Message: msg,
}
}

// ToRegServiceDeployFailure condition when an error occurred
func ToRegServiceDeployFailure(msg string) toolchainv1alpha1.Condition {
return toolchainv1alpha1.Condition{
Expand Down
18 changes: 9 additions & 9 deletions controllers/toolchainconfig/toolchainconfig_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestReconcile(t *testing.T) {
Exists().
HasConditions(
toolchainconfig.ToSyncComplete(),
toolchainconfig.ToRegServiceDeploying("updated resources: [ServiceAccount: registration-service Role: registration-service RoleBinding: registration-service Deployment: registration-service Route: registration-service Service: registration-service Service: registration-service-metrics Route: api Service: api Service: proxy-metrics-service]")).
toolchainconfig.ToRegServiceDeployComplete()).
HasNoSyncErrors()

// check member1 config
Expand Down Expand Up @@ -233,8 +233,8 @@ func TestReconcile(t *testing.T) {
config := commonconfig.NewToolchainConfigObjWithReset(t,
testconfig.AutomaticApproval().Enabled(true))
hostCl := test.NewFakeClient(t, config)
hostCl.MockCreate = func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.CreateOption) error {
return fmt.Errorf("create error")
hostCl.MockPatch = func(ctx context.Context, obj runtimeclient.Object, patch runtimeclient.Patch, opts ...runtimeclient.PatchOption) error {
return fmt.Errorf("patch error")
}
members := NewGetMemberClusters(NewMemberClusterWithTenantRole(t, "member1", corev1.ConditionTrue), NewMemberClusterWithTenantRole(t, "member2", corev1.ConditionTrue))
controller := newController(t, hostCl, members)
Expand All @@ -243,14 +243,14 @@ func TestReconcile(t *testing.T) {
_, err := controller.Reconcile(context.TODO(), newRequest())

// then
require.EqualError(t, err, "failed to apply registration service object registration-service: unable to create resource of kind: ServiceAccount, version: v1: create error")
require.EqualError(t, err, "failed to apply registration service: unable to patch '/v1, Kind=ServiceAccount' called 'registration-service' in namespace 'toolchain-host-operator': patch error")
actual, err := toolchainconfig.GetToolchainConfig(hostCl)
require.NoError(t, err)
assert.True(t, actual.AutomaticApproval().IsEnabled())
testconfig.AssertThatToolchainConfig(t, test.HostOperatorNs, hostCl).
Exists().
HasConditions(
toolchainconfig.ToRegServiceDeployFailure("failed to apply registration service object registration-service: unable to create resource of kind: ServiceAccount, version: v1: create error")).
toolchainconfig.ToRegServiceDeployFailure("failed to apply registration service: unable to patch '/v1, Kind=ServiceAccount' called 'registration-service' in namespace 'toolchain-host-operator': patch error")).
HasNoSyncErrors()
})

Expand All @@ -274,7 +274,7 @@ func TestReconcile(t *testing.T) {
Exists().
HasConditions(
toolchainconfig.ToSyncFailure(),
toolchainconfig.ToRegServiceDeploying("updated resources: [ServiceAccount: registration-service Role: registration-service RoleBinding: registration-service Deployment: registration-service Route: registration-service Service: registration-service Service: registration-service-metrics Route: api Service: api Service: proxy-metrics-service]")).
toolchainconfig.ToRegServiceDeployComplete()).
HasSyncErrors(map[string]string{"missing-member": "specific member configuration exists but no matching toolchaincluster was found"})
})
})
Expand All @@ -296,7 +296,7 @@ func TestWrapErrorWithUpdateStatus(t *testing.T) {
}

// test
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, nil, "failed to load the latest configuration")
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, nil)

require.NoError(t, err)
})
Expand All @@ -308,7 +308,7 @@ func TestWrapErrorWithUpdateStatus(t *testing.T) {
}

// test
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, fmt.Errorf("underlying error"), "failed to load the latest configuration")
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, fmt.Errorf("failed to load the latest configuration: %w", fmt.Errorf("underlying error")))

require.EqualError(t, err, "failed to load the latest configuration: underlying error")
})
Expand All @@ -319,7 +319,7 @@ func TestWrapErrorWithUpdateStatus(t *testing.T) {
}

// when
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, fmt.Errorf("underlying error"), "failed to load the latest configuration")
err := controller.WrapErrorWithStatusUpdate(ctx, config, statusUpdater, fmt.Errorf("failed to load the latest configuration: %w", fmt.Errorf("underlying error")))

// then
require.EqualError(t, err, "failed to load the latest configuration: underlying error")
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/codeready-toolchain/host-operator
require (
cloud.google.com/go/recaptchaenterprise/v2 v2.13.0
github.com/codeready-toolchain/api v0.0.0-20250313170542-4e3c4147cb80
github.com/codeready-toolchain/toolchain-common v0.0.0-20250313203311-0bce6563576f
github.com/codeready-toolchain/toolchain-common v0.0.0-20250430091615-95cf792ac171
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/go-logr/logr v1.4.1
Expand Down Expand Up @@ -31,6 +31,8 @@ require (
sigs.k8s.io/controller-runtime v0.18.4
)

require k8s.io/kubectl v0.30.1

require (
cloud.google.com/go/auth v0.3.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
Expand Down Expand Up @@ -117,7 +119,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/cli-runtime v0.30.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/kubectl v0.30.1 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/codeready-toolchain/api v0.0.0-20250313170542-4e3c4147cb80 h1:wh41dZ7VkyClQWiMJbMypku6xTxtxNZTgQJUAcodgUo=
github.com/codeready-toolchain/api v0.0.0-20250313170542-4e3c4147cb80/go.mod h1:shTTQ+bYWinbdF/oK+ly4DO5jdXtIASZ+uPZElsmsKg=
github.com/codeready-toolchain/toolchain-common v0.0.0-20250313203311-0bce6563576f h1:qBUZ/xBNgDMf5Y0tObJ85/9GcDSgtUf8WFfCrNSxaZ4=
github.com/codeready-toolchain/toolchain-common v0.0.0-20250313203311-0bce6563576f/go.mod h1:0dopWh1MWi9eg2d5RAd4uSUmXfDMqz4Yk2oNPHF15+Y=
github.com/codeready-toolchain/toolchain-common v0.0.0-20250430091615-95cf792ac171 h1:qK+3ECtkMiWT8IyPhwetajh04E/XRSzC2NN5PQnSDSM=
github.com/codeready-toolchain/toolchain-common v0.0.0-20250430091615-95cf792ac171/go.mod h1:Rd64iFrlcocihpLq4qIsCMkjN/1invHHaU1AMq19OWA=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
Loading