Skip to content

Commit

Permalink
Simplify step functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ccremer committed Jul 8, 2022
1 parent 6d954de commit 728fed8
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 78 deletions.
96 changes: 45 additions & 51 deletions operator/cloudscale/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,72 +34,66 @@ func CreateCloudscaleClientFn(apiToken string) func(ctx context.Context) error {
// CloudscaleUserKey identifies the User object from cloudscale SDK in the context.
type CloudscaleUserKey struct{}

// CreateObjectsUserFn creates a new objects user in the project associated with the API token.
func CreateObjectsUserFn() func(ctx context.Context) error {
return func(ctx context.Context) error {
csClient := steps.GetFromContextOrPanic(ctx, CloudscaleClientKey{}).(*cloudscalesdk.Client)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)
// CreateObjectsUser creates a new objects user in the project associated with the API token.
func CreateObjectsUser(ctx context.Context) error {
csClient := steps.GetFromContextOrPanic(ctx, CloudscaleClientKey{}).(*cloudscalesdk.Client)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)

displayName := fmt.Sprintf("%s.%s", user.Namespace, user.Name)
displayName := fmt.Sprintf("%s.%s", user.Namespace, user.Name)

csUser, err := csClient.ObjectsUsers.Create(ctx, &cloudscalesdk.ObjectsUserRequest{
DisplayName: displayName,
})
user.Status.UserID = csUser.ID
csUser, err := csClient.ObjectsUsers.Create(ctx, &cloudscalesdk.ObjectsUserRequest{
DisplayName: displayName,
})
user.Status.UserID = csUser.ID

pipeline.StoreInContext(ctx, CloudscaleUserKey{}, csUser)
return logIfNotError(err, log, 1, "Created objects user in cloudscale")
}
pipeline.StoreInContext(ctx, CloudscaleUserKey{}, csUser)
return logIfNotError(err, log, 1, "Created objects user in cloudscale")
}

// GetObjectsUserFn fetches an existing objects user from the project associated with the API token.
func GetObjectsUserFn() func(ctx context.Context) error {
return func(ctx context.Context) error {
csClient := steps.GetFromContextOrPanic(ctx, CloudscaleClientKey{}).(*cloudscalesdk.Client)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)
// GetObjectsUser fetches an existing objects user from the project associated with the API token.
func GetObjectsUser(ctx context.Context) error {
csClient := steps.GetFromContextOrPanic(ctx, CloudscaleClientKey{}).(*cloudscalesdk.Client)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)

csUser, err := csClient.ObjectsUsers.Get(ctx, user.Status.UserID)
csUser, err := csClient.ObjectsUsers.Get(ctx, user.Status.UserID)

pipeline.StoreInContext(ctx, CloudscaleUserKey{}, csUser)
return logIfNotError(err, log, 1, "Fetched objects user in cloudscale")
}
pipeline.StoreInContext(ctx, CloudscaleUserKey{}, csUser)
return logIfNotError(err, log, 1, "Fetched objects user in cloudscale")
}

// UserCredentialSecretKey identifies the credential Secret in the context.
type UserCredentialSecretKey struct{}

// EnsureCredentialSecretFn creates the credential secret.
func EnsureCredentialSecretFn() func(ctx context.Context) error {
return func(ctx context.Context) error {
kube := steps.GetClientFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
csUser := steps.GetFromContextOrPanic(ctx, CloudscaleUserKey{}).(*cloudscalesdk.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)

secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: user.Spec.SecretRef, Namespace: user.Namespace}}
// EnsureCredentialSecret creates the credential secret.
func EnsureCredentialSecret(ctx context.Context) error {
kube := steps.GetClientFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
csUser := steps.GetFromContextOrPanic(ctx, CloudscaleUserKey{}).(*cloudscalesdk.ObjectsUser)
log := controllerruntime.LoggerFrom(ctx)

if keyErr := checkUserForKeys(csUser); keyErr != nil {
return keyErr
}
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: user.Spec.SecretRef, Namespace: user.Namespace}}

// See https://www.cloudscale.ch/en/api/v1#objects-users

_, err := controllerruntime.CreateOrUpdate(ctx, kube, secret, func() error {
secret.Labels = labels.Merge(secret.Labels, getCommonLabels(user.Name))
if secret.StringData == nil {
secret.StringData = map[string]string{}
}
secret.StringData["AWS_ACCESS_KEY_ID"] = csUser.Keys[0]["access_key"]
secret.StringData["AWS_SECRET_ACCESS_KEY"] = csUser.Keys[0]["secret_key"]
controllerutil.AddFinalizer(secret, userFinalizer)
return controllerutil.SetOwnerReference(user, secret, kube.Scheme())
})

pipeline.StoreInContext(ctx, UserCredentialSecretKey{}, secret)
return logIfNotError(err, log, 1, "Ensured credential secret", "secretName", user.Spec.SecretRef)
if keyErr := checkUserForKeys(csUser); keyErr != nil {
return keyErr
}

// See https://www.cloudscale.ch/en/api/v1#objects-users

_, err := controllerruntime.CreateOrUpdate(ctx, kube, secret, func() error {
secret.Labels = labels.Merge(secret.Labels, getCommonLabels(user.Name))
if secret.StringData == nil {
secret.StringData = map[string]string{}
}
secret.StringData["AWS_ACCESS_KEY_ID"] = csUser.Keys[0]["access_key"]
secret.StringData["AWS_SECRET_ACCESS_KEY"] = csUser.Keys[0]["secret_key"]
controllerutil.AddFinalizer(secret, userFinalizer)
return controllerutil.SetOwnerReference(user, secret, kube.Scheme())
})

pipeline.StoreInContext(ctx, UserCredentialSecretKey{}, secret)
return logIfNotError(err, log, 1, "Ensured credential secret", "secretName", user.Spec.SecretRef)
}

func checkUserForKeys(user *cloudscalesdk.ObjectsUser) error {
Expand Down
2 changes: 1 addition & 1 deletion operator/cloudscale/client_it_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (ts *CloudscaleClientSuite) Test_EnsureCredentialSecretFn() {
ts.EnsureNS(user.Namespace)

// Act
err := EnsureCredentialSecretFn()(ts.Context)
err := EnsureCredentialSecret(ts.Context)
ts.Require().NoError(err)

// Assert
Expand Down
46 changes: 20 additions & 26 deletions operator/cloudscale/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,48 +30,42 @@ func (p *ObjectsUserPipeline) Run(ctx context.Context) error {
WithSteps(
pipeline.NewStepFromFunc("add finalizer", steps.AddFinalizerFn(ObjectsUserKey{}, userFinalizer)),
pipeline.NewStepFromFunc("create client", CreateCloudscaleClientFn(APIToken)),
pipeline.IfOrElse(isObjectsUserIDKnown(),
pipeline.NewStepFromFunc("fetch objects user", GetObjectsUserFn()),
pipeline.IfOrElse(isObjectsUserIDKnown,
pipeline.NewStepFromFunc("fetch objects user", GetObjectsUser),
pipeline.NewPipeline().WithNestedSteps("new user",
pipeline.NewStepFromFunc("create objects user", CreateObjectsUserFn()),
pipeline.NewStepFromFunc("create objects user", CreateObjectsUser),
pipeline.NewStepFromFunc("set user in status", steps.UpdateStatusFn(ObjectsUserKey{})),
pipeline.NewStepFromFunc("emit event", emitSuccessEventFn()),
pipeline.NewStepFromFunc("emit event", emitSuccessEvent),
),
),
pipeline.NewStepFromFunc("ensure credential secret", EnsureCredentialSecretFn()),
pipeline.NewStepFromFunc("set status condition", markUserReadyFn()),
pipeline.NewStepFromFunc("ensure credential secret", EnsureCredentialSecret),
pipeline.NewStepFromFunc("set status condition", markUserReady),
).
WithFinalizer(errorHandler())
result := pipe.RunWithContext(ctx)
return result.Err()
}

func isObjectsUserIDKnown() func(ctx context.Context) bool {
return func(ctx context.Context) bool {
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
return user.Status.UserID != ""
}
func isObjectsUserIDKnown(ctx context.Context) bool {
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
return user.Status.UserID != ""
}

func emitSuccessEventFn() func(ctx context.Context) error {
return func(ctx context.Context) error {
recorder := steps.GetEventRecorderFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(client.Object)
func emitSuccessEvent(ctx context.Context) error {
recorder := steps.GetEventRecorderFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(client.Object)

recorder.Event(user, v1.EventTypeNormal, "Created", "ObjectsUser successfully created")
return nil
}
recorder.Event(user, v1.EventTypeNormal, "Created", "ObjectsUser successfully created")
return nil
}

func markUserReadyFn() func(ctx context.Context) error {
return func(ctx context.Context) error {
kube := steps.GetClientFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)
func markUserReady(ctx context.Context) error {
kube := steps.GetClientFromContext(ctx)
user := steps.GetFromContextOrPanic(ctx, ObjectsUserKey{}).(*cloudscalev1.ObjectsUser)

meta.SetStatusCondition(&user.Status.Conditions, conditions.Ready())
meta.RemoveStatusCondition(&user.Status.Conditions, conditions.TypeFailed)
return kube.Status().Update(ctx, user)
}
meta.SetStatusCondition(&user.Status.Conditions, conditions.Ready())
meta.RemoveStatusCondition(&user.Status.Conditions, conditions.TypeFailed)
return kube.Status().Update(ctx, user)
}

func getCommonLabels(instanceName string) labels.Set {
Expand Down

0 comments on commit 728fed8

Please sign in to comment.