diff --git a/api/formance.com/v1beta1/shared.go b/api/formance.com/v1beta1/shared.go index 5894e65c1..003b07964 100644 --- a/api/formance.com/v1beta1/shared.go +++ b/api/formance.com/v1beta1/shared.go @@ -9,6 +9,7 @@ import ( "golang.org/x/mod/semver" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/formancehq/go-libs/v5/pkg/types/pointer" @@ -343,7 +344,7 @@ func (u *URI) UnmarshalJSON(data []byte) error { v, err := url.Parse(s) if err != nil { - panic(err) + return err } *u = URI{ @@ -372,7 +373,7 @@ func ParseURL(v string) (*URI, error) { } func init() { - if err := equality.Semantic.AddFunc(func(a, b *URI) bool { + utilruntime.Must(equality.Semantic.AddFunc(func(a, b *URI) bool { if a == nil && b != nil { return false } @@ -383,9 +384,7 @@ func init() { return true } return a.String() == b.String() - }); err != nil { - panic(err) - } + })) } const ( diff --git a/docs/09-Configuration reference/settings.catalog.json b/docs/09-Configuration reference/settings.catalog.json index a4316ca86..52cc2d408 100644 --- a/docs/09-Configuration reference/settings.catalog.json +++ b/docs/09-Configuration reference/settings.catalog.json @@ -12,7 +12,7 @@ "key": "auth.issuers", "valueType": "string", "sources": [ - "internal/resources/auths/deployment.go:69", + "internal/resources/auths/deployment.go:77", "internal/resources/auths/env.go:34" ] }, @@ -366,7 +366,7 @@ "key": "ledger.api.bulk-max-size", "valueType": "int", "sources": [ - "internal/resources/ledgers/deployments.go:123" + "internal/resources/ledgers/deployments.go:128" ] }, { @@ -387,8 +387,8 @@ "key": "ledger.experimental-exporters", "valueType": "bool", "sources": [ - "internal/resources/ledgers/deployments.go:149", - "internal/resources/ledgers/deployments.go:264" + "internal/resources/ledgers/deployments.go:154", + "internal/resources/ledgers/deployments.go:269" ] }, { @@ -416,8 +416,8 @@ "key": "ledger.schema-enforcement-mode", "valueType": "string", "sources": [ - "internal/resources/ledgers/deployments.go:131", - "internal/resources/ledgers/deployments.go:239" + "internal/resources/ledgers/deployments.go:136", + "internal/resources/ledgers/deployments.go:244" ] }, { @@ -435,7 +435,7 @@ } ], "sources": [ - "internal/resources/ledgers/deployments.go:209" + "internal/resources/ledgers/deployments.go:214" ] }, { @@ -453,7 +453,7 @@ } ], "sources": [ - "internal/resources/ledgers/deployments.go:248" + "internal/resources/ledgers/deployments.go:253" ] }, { @@ -479,7 +479,7 @@ } ], "sources": [ - "internal/resources/ledgers/deployments.go:221" + "internal/resources/ledgers/deployments.go:226" ] }, { @@ -519,14 +519,14 @@ "key": "namespace.annotations", "valueType": "map[string]string", "sources": [ - "internal/resources/stacks/init.go:173" + "internal/resources/stacks/init.go:177" ] }, { "key": "namespace.labels", "valueType": "map[string]string", "sources": [ - "internal/resources/stacks/init.go:154" + "internal/resources/stacks/init.go:158" ] }, { @@ -562,7 +562,7 @@ "valueType": "int", "default": "10", "sources": [ - "internal/resources/orchestrations/deployments.go:173" + "internal/resources/orchestrations/deployments.go:182" ] }, { @@ -650,7 +650,7 @@ "key": "temporal.dsn", "valueType": "uri", "sources": [ - "internal/resources/orchestrations/deployments.go:80", + "internal/resources/orchestrations/deployments.go:84", "internal/resources/payments/deployments.go:43" ] }, @@ -658,7 +658,7 @@ "key": "temporal.tls.crt", "valueType": "string", "sources": [ - "internal/resources/orchestrations/deployments.go:124", + "internal/resources/orchestrations/deployments.go:128", "internal/resources/payments/deployments.go:81" ] }, @@ -666,7 +666,7 @@ "key": "temporal.tls.key", "valueType": "string", "sources": [ - "internal/resources/orchestrations/deployments.go:129", + "internal/resources/orchestrations/deployments.go:133", "internal/resources/payments/deployments.go:89" ] }, @@ -675,7 +675,7 @@ "valueType": "bool", "default": "false", "sources": [ - "internal/resources/transactionplane/deployments.go:125" + "internal/resources/transactionplane/deployments.go:134" ] } ] diff --git a/internal/core/module.go b/internal/core/module.go index aca96906c..0ee4cb51c 100644 --- a/internal/core/module.go +++ b/internal/core/module.go @@ -7,18 +7,18 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func LowerCamelCaseKind(ctx Context, ob client.Object) string { +func LowerCamelCaseKind(ctx Context, ob client.Object) (string, error) { kinds, _, err := ctx.GetScheme().ObjectKinds(ob) if err != nil { - panic(err) + return "", err } - return strcase.ToLowerCamel(kinds[0].Kind) + return strcase.ToLowerCamel(kinds[0].Kind), nil } -func LowerCaseKind(ctx Context, ob client.Object) string { +func LowerCaseKind(ctx Context, ob client.Object) (string, error) { kinds, _, err := ctx.GetScheme().ObjectKinds(ob) if err != nil { - panic(err) + return "", err } - return strings.ToLower(kinds[0].Kind) + return strings.ToLower(kinds[0].Kind), nil } diff --git a/internal/core/reconciler.go b/internal/core/reconciler.go index 00824b708..2bc3ca742 100644 --- a/internal/core/reconciler.go +++ b/internal/core/reconciler.go @@ -403,16 +403,19 @@ func WithModuleReconciler[T v1beta1.Module](fn func(ctx Context, stack *v1beta1. func WithWatchVersions[T client.Object](options *ReconcilerOptions[T]) { reconcileModule := func(ctx context.Context, mgr Manager, target client.Object, versionFileName string, limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) { + logger := log.FromContext(ctx) stackList := &v1beta1.StackList{} if err := mgr.GetClient().List(ctx, stackList, client.MatchingFields{ ".spec.versionsFromFile": versionFileName, }); err != nil { - panic(err) + logger.Error(err, "listing stacks for versions file, dropping event", "versionsFile", versionFileName) + return } kinds, _, err := mgr.GetScheme().ObjectKinds(target) if err != nil { - panic(err) + logger.Error(err, "resolving object kind, dropping event", "target", fmt.Sprintf("%T", target)) + return } for _, stack := range stackList.Items { @@ -421,7 +424,8 @@ func WithWatchVersions[T client.Object](options *ReconcilerOptions[T]) { if err := mgr.GetClient().List(ctx, list, client.MatchingFields{ "stack": stack.Name, }); err != nil { - panic(err) + logger.Error(err, "listing modules for stack, skipping it", "stack", stack.Name) + continue } for _, item := range list.Items { @@ -446,7 +450,8 @@ func WithWatchVersions[T client.Object](options *ReconcilerOptions[T]) { kinds, _, err := mgr.GetScheme().ObjectKinds(target) if err != nil { - panic(err) + log.FromContext(ctx).Error(err, "resolving object kind, dropping event", "target", fmt.Sprintf("%T", target)) + return } kind := strings.ToLower(kinds[0].Kind) if oldObject.Spec[kind] == newObject.Spec[kind] { diff --git a/internal/core/stacks.go b/internal/core/stacks.go index f9e848bb1..28f93f614 100644 --- a/internal/core/stacks.go +++ b/internal/core/stacks.go @@ -37,7 +37,7 @@ func GetAllStackDependencies(ctx Context, stackName string, to any) error { for _, item := range list.Items { t := reflect.New(objectType.Elem()).Interface().(client.Object) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, t); err != nil { - panic(err) + return errors.Wrapf(err, "converting unstructured object '%s' to %s", item.GetName(), objectType) } ret = reflect.Append(ret, reflect.ValueOf(t)) } diff --git a/internal/core/utils.go b/internal/core/utils.go index 8b16454f7..da5153ea0 100644 --- a/internal/core/utils.go +++ b/internal/core/utils.go @@ -6,8 +6,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/fs" - "path/filepath" "reflect" "runtime" "strings" @@ -26,14 +24,14 @@ import ( "github.com/formancehq/go-libs/v5/pkg/types/pointer" ) -func HashFromConfigMaps(configMaps ...*corev1.ConfigMap) string { +func HashFromConfigMaps(configMaps ...*corev1.ConfigMap) (string, error) { digest := sha256.New() for _, configMap := range configMaps { if err := json.NewEncoder(digest).Encode(configMap.Data); err != nil { - panic(err) + return "", err } } - return base64.StdEncoding.EncodeToString(digest.Sum(nil)) + return base64.StdEncoding.EncodeToString(digest.Sum(nil)), nil } func HashFromResources(resources ...*unstructured.Unstructured) string { @@ -48,27 +46,6 @@ func HashFromResources(resources ...*unstructured.Unstructured) string { return base64.StdEncoding.EncodeToString(digest.Sum(nil)) } -func CopyDir(f fs.FS, root, path string, ret *map[string]string) { - dirEntries, err := fs.ReadDir(f, path) - if err != nil { - panic(err) - } - for _, dirEntry := range dirEntries { - dirEntryPath := filepath.Join(path, dirEntry.Name()) - if dirEntry.IsDir() { - CopyDir(f, root, dirEntryPath, ret) - } else { - fileContent, err := fs.ReadFile(f, dirEntryPath) - if err != nil { - panic(err) - } - sanitizedPath := strings.TrimPrefix(dirEntryPath, root) - sanitizedPath = strings.TrimPrefix(sanitizedPath, "/") - (*ret)[sanitizedPath] = string(fileContent) - } - } -} - type ObjectMutator[T any] func(t T) error func WithController[T client.Object](scheme *k8sruntime.Scheme, owner client.Object) ObjectMutator[T] { @@ -84,11 +61,7 @@ func WithController[T client.Object](scheme *k8sruntime.Scheme, owner client.Obj func WithOwner[T client.Object](scheme *k8sruntime.Scheme, owner client.Object) ObjectMutator[T] { return func(t T) error { - if err := controllerutil.SetOwnerReference(owner, t, scheme); err != nil { - panic(err) - } - - return nil + return controllerutil.SetOwnerReference(owner, t, scheme) } } diff --git a/internal/resources/auths/deployment.go b/internal/resources/auths/deployment.go index 5b9f84e73..bdfc53dab 100644 --- a/internal/resources/auths/deployment.go +++ b/internal/resources/auths/deployment.go @@ -22,24 +22,32 @@ import ( "github.com/formancehq/operator/v3/internal/resources/settings" ) -func HashFromHash(o ...string) string { +func HashFromHash(o ...string) (string, error) { digest := sha256.New() for _, h := range o { if err := json.NewEncoder(digest).Encode(h); err != nil { - panic(err) + return "", err } } - return base64.StdEncoding.EncodeToString(digest.Sum(nil)) + return base64.StdEncoding.EncodeToString(digest.Sum(nil)), nil } func createDeployment(ctx Context, stack *v1beta1.Stack, auth *v1beta1.Auth, database *v1beta1.Database, configMap *corev1.ConfigMap, imageConfiguration *registries.ImageConfiguration, version string, clients []*v1beta1.AuthClient) error { + configHash, err := HashFromConfigMaps(configMap) + if err != nil { + return err + } annotations := map[string]string{ - "config-hash": HashFromConfigMaps(configMap), + "config-hash": configHash, } env := make([]corev1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, LowerCamelCaseKind(ctx, auth), " ") + serviceName, err := LowerCamelCaseKind(ctx, auth) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } @@ -132,7 +140,11 @@ func createDeployment(ctx Context, stack *v1beta1.Stack, auth *v1beta1.Auth, dat } return acc }, hashList) - annotations["auth-clients-secrets"] = HashFromHash(hashList...) + authClientsSecretsHash, err := HashFromHash(hashList...) + if err != nil { + return err + } + annotations["auth-clients-secrets"] = authClientsSecretsHash for _, client := range clients { if client.Spec.SecretFromSecret != nil { env = append(env, AuthClientSecretToEnvVars(client)) diff --git a/internal/resources/benthos/controller.go b/internal/resources/benthos/controller.go index 77a740c71..eb1e85f74 100644 --- a/internal/resources/benthos/controller.go +++ b/internal/resources/benthos/controller.go @@ -132,6 +132,31 @@ func reconcileEnvFromResourceReferences(ctx Context, b *v1beta1.Benthos) (map[st } // We need to this controller and keep it focused on benthos +// buildTemplates merges the user-provided templates with the builtin ones +// shipped with the operator. +func buildTemplates(b *v1beta1.Benthos) (map[string]string, error) { + ret := b.Spec.Templates + if ret == nil { + ret = make(map[string]string) + } + + files, err := builtinTemplates.ReadDir("builtin-templates") + if err != nil { + return nil, errors.Wrap(err, "reading builtin templates directory") + } + + for _, file := range files { + data, err := builtinTemplates.ReadFile("builtin-templates/" + file.Name()) + if err != nil { + return nil, errors.Wrapf(err, "reading builtin template '%s'", file.Name()) + } + + ret[file.Name()] = string(data) + } + + return ret, nil +} + func createDeployment(ctx Context, stack *v1beta1.Stack, b *v1beta1.Benthos) error { serviceAccountName, err := settings.GetAWSServiceAccount(ctx, stack.Name) if err != nil { @@ -225,6 +250,11 @@ func createDeployment(ctx Context, stack *v1beta1.Stack, b *v1beta1.Benthos) err volumeMounts := make([]corev1.VolumeMount, 0) configMaps := make([]*corev1.ConfigMap, 0) + templates, err := buildTemplates(b) + if err != nil { + return err + } + for _, object := range []struct { discr string files map[string]string @@ -235,28 +265,7 @@ func createDeployment(ctx Context, stack *v1beta1.Stack, b *v1beta1.Benthos) err }, { discr: "templates", - files: func() map[string]string { - ret := b.Spec.Templates - if ret == nil { - ret = make(map[string]string) - } - - files, err := builtinTemplates.ReadDir("builtin-templates") - if err != nil { - panic(err) - } - - for _, file := range files { - data, err := builtinTemplates.ReadFile("builtin-templates/" + file.Name()) - if err != nil { - panic(err) - } - - ret[file.Name()] = string(data) - } - - return ret - }(), + files: templates, }, } { @@ -319,7 +328,7 @@ func createDeployment(ctx Context, stack *v1beta1.Stack, b *v1beta1.Benthos) err digest := sha256.New() for _, configMap := range configMaps { if err := json.NewEncoder(digest).Encode(configMap.Data); err != nil { - panic(err) + return errors.Wrap(err, "hashing config map data") } } for _, stream := range streams { diff --git a/internal/resources/benthosstreams/init.go b/internal/resources/benthosstreams/init.go index 7da5b6c4c..adbcc0b96 100644 --- a/internal/resources/benthosstreams/init.go +++ b/internal/resources/benthosstreams/init.go @@ -57,7 +57,11 @@ func Reconcile(ctx Context, _ *v1beta1.Stack, stream *v1beta1.BenthosStream) err return err } - stream.Status.ConfigMapHash = HashFromConfigMaps(cm) + configMapHash, err := HashFromConfigMaps(cm) + if err != nil { + return err + } + stream.Status.ConfigMapHash = configMapHash return nil } diff --git a/internal/resources/brokers/utils.go b/internal/resources/brokers/utils.go index eded4d49b..f61fe4838 100644 --- a/internal/resources/brokers/utils.go +++ b/internal/resources/brokers/utils.go @@ -68,12 +68,12 @@ func GetBrokerEnvVars(ctx core.Context, brokerURI *v1beta1.URI, stackName, servi return ret, nil } -func GetPublisherEnvVars(stack *v1beta1.Stack, broker *v1beta1.Broker, service string) []v1.EnvVar { +func GetPublisherEnvVars(stack *v1beta1.Stack, broker *v1beta1.Broker, service string) ([]v1.EnvVar, error) { switch broker.Status.Mode { case v1beta1.ModeOneStreamByService: return []v1.EnvVar{ core.Env("PUBLISHER_TOPIC_MAPPING", "*:"+core.GetObjectName(stack.Name, service)), - } + }, nil case v1beta1.ModeOneStreamByStack: ret := []v1.EnvVar{ core.Env("PUBLISHER_TOPIC_MAPPING", fmt.Sprintf("*:%s.%s", stack.Name, service)), @@ -82,9 +82,9 @@ func GetPublisherEnvVars(stack *v1beta1.Stack, broker *v1beta1.Broker, service s if broker.Status.URI.Scheme == "nats" { ret = append(ret, core.Env("PUBLISHER_NATS_AUTO_PROVISION", "false")) } - return ret + return ret, nil default: - panic(fmt.Sprintf("mode '%s' not handled", broker.Status.Mode)) + return nil, fmt.Errorf("broker mode '%s' not handled", broker.Status.Mode) } } diff --git a/internal/resources/caddy/caddy.go b/internal/resources/caddy/caddy.go index 3c2fc9ca8..5561e1223 100644 --- a/internal/resources/caddy/caddy.go +++ b/internal/resources/caddy/caddy.go @@ -29,7 +29,11 @@ func DeploymentTemplate( ) (*appsv1.Deployment, error) { t := &appsv1.Deployment{} - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, owner), ",") + serviceName, err := core.LowerCamelCaseKind(ctx, owner) + if err != nil { + return nil, err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, ",") if err != nil { return nil, err } @@ -48,8 +52,12 @@ func DeploymentTemplate( env = append(env, core.Env("OTEL_EXPORTER_OTLP_PROTOCOL", "$(OTEL_TRACES_EXPORTER_OTLP_MODE)")) } + caddyfileHash, err := core.HashFromConfigMaps(caddyfile) + if err != nil { + return nil, err + } t.Spec.Template.Annotations = collectionutils.MergeMaps(t.Spec.Template.Annotations, map[string]string{ - "caddyfile-hash": core.HashFromConfigMaps(caddyfile), + "caddyfile-hash": caddyfileHash, }) t.Spec.Template.Spec.Volumes = []v1.Volume{ volumeFromConfigMap("caddyfile", caddyfile), diff --git a/internal/resources/databases/watch.go b/internal/resources/databases/watch.go index ce1347ec8..70aa5c119 100644 --- a/internal/resources/databases/watch.go +++ b/internal/resources/databases/watch.go @@ -4,6 +4,7 @@ import ( "reflect" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/formancehq/operator/v3/api/formance.com/v1beta1" @@ -14,13 +15,18 @@ func Watch[T client.Object]() core.ReconcilerOption[T] { var t T t = reflect.New(reflect.TypeOf(t).Elem()).Interface().(T) return core.WithWatch[T, *v1beta1.Database](func(ctx core.Context, database *v1beta1.Database) []reconcile.Request { - if database.Spec.Service != core.LowerCamelCaseKind(ctx, t) { + serviceName, err := core.LowerCamelCaseKind(ctx, t) + if err != nil { + log.FromContext(ctx).Error(err, "resolving object kind, dropping event") + return []reconcile.Request{} + } + if database.Spec.Service != serviceName { return []reconcile.Request{} } slice := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(t)), 0, 0).Interface() - err := core.GetAllStackDependencies(ctx, database.Spec.Stack, &slice) + err = core.GetAllStackDependencies(ctx, database.Spec.Stack, &slice) if err != nil { return []reconcile.Request{} } diff --git a/internal/resources/gatewayhttpapis/create.go b/internal/resources/gatewayhttpapis/create.go index e843b8235..9d9350a82 100644 --- a/internal/resources/gatewayhttpapis/create.go +++ b/internal/resources/gatewayhttpapis/create.go @@ -14,9 +14,12 @@ var defaultOptions = []option{ } func Create(ctx core.Context, owner v1beta1.Module, options ...option) error { - objectName := core.LowerCaseKind(ctx, owner) - _, _, err := core.CreateOrUpdate[*v1beta1.GatewayHTTPAPI](ctx, types.NamespacedName{ - Name: core.GetObjectName(owner.GetStack(), core.LowerCaseKind(ctx, owner)), + objectName, err := core.LowerCaseKind(ctx, owner) + if err != nil { + return err + } + _, _, err = core.CreateOrUpdate[*v1beta1.GatewayHTTPAPI](ctx, types.NamespacedName{ + Name: core.GetObjectName(owner.GetStack(), objectName), }, func(t *v1beta1.GatewayHTTPAPI) error { t.Spec = v1beta1.GatewayHTTPAPISpec{ diff --git a/internal/resources/ledgers/deployments.go b/internal/resources/ledgers/deployments.go index 2e6249c30..4163ab2de 100644 --- a/internal/resources/ledgers/deployments.go +++ b/internal/resources/ledgers/deployments.go @@ -117,7 +117,12 @@ func installLedgerStateless(ctx core.Context, stack *v1beta1.Stack, ledger *v1be } container.Env = append(container.Env, brokerEnvVar...) - container.Env = append(container.Env, brokers.GetPublisherEnvVars(stack, broker, "ledger")...) + + publisherEnvVars, err := brokers.GetPublisherEnvVars(stack, broker, "ledger") + if err != nil { + return err + } + container.Env = append(container.Env, publisherEnvVars...) } bulkMaxSize, err := settings.GetInt(ctx, stack.Name, "ledger", "api", "bulk-max-size") @@ -342,7 +347,11 @@ func uninstallLedgerMonoWriterMultipleReader(ctx core.Context, stack *v1beta1.St func setCommonContainerConfiguration(ctx core.Context, stack *v1beta1.Stack, ledger *v1beta1.Ledger, imageConfiguration *registries.ImageConfiguration, database *v1beta1.Database, container *corev1.Container) error { env := make([]corev1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, ledger), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, ledger) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } diff --git a/internal/resources/orchestrations/deployments.go b/internal/resources/orchestrations/deployments.go index 633779ffe..930c6cca9 100644 --- a/internal/resources/orchestrations/deployments.go +++ b/internal/resources/orchestrations/deployments.go @@ -57,7 +57,11 @@ func createDeployment( ) error { env := make([]corev1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, LowerCamelCaseKind(ctx, orchestration), " ") + serviceName, err := LowerCamelCaseKind(ctx, orchestration) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } @@ -158,7 +162,12 @@ func createDeployment( return err } env = append(env, brokerEnvVars...) - env = append(env, brokers.GetPublisherEnvVars(stack, broker, "orchestration")...) + + publisherEnvVars, err := brokers.GetPublisherEnvVars(stack, broker, "orchestration") + if err != nil { + return err + } + env = append(env, publisherEnvVars...) serviceAccountName, err := settings.GetAWSServiceAccount(ctx, stack.Name) if err != nil { diff --git a/internal/resources/payments/deployments.go b/internal/resources/payments/deployments.go index 1cb54db65..bb0b36475 100644 --- a/internal/resources/payments/deployments.go +++ b/internal/resources/payments/deployments.go @@ -151,7 +151,11 @@ func temporalEnvVars(ctx core.Context, stack *v1beta1.Stack, payments *v1beta1.P func commonEnvVars(ctx core.Context, stack *v1beta1.Stack, payments *v1beta1.Payments, database *v1beta1.Database) ([]corev1.EnvVar, error) { env := make([]corev1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, payments), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, payments) + if err != nil { + return nil, err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return nil, err } @@ -385,7 +389,12 @@ func v3EnvVars( } envVars = append(envVars, additionalEnv...) - envVars = append(envVars, brokers.GetPublisherEnvVars(stack, broker, "payments")...) + + additionalEnv, err = brokers.GetPublisherEnvVars(stack, broker, "payments") + if err != nil { + return + } + envVars = append(envVars, additionalEnv...) } hash, additionalEnv, err = temporalEnvVars(ctx, stack, payments) @@ -481,7 +490,12 @@ func createV2ConnectorsDeployment(ctx core.Context, stack *v1beta1.Stack, paymen } env = append(env, brokerEnvVar...) - env = append(env, brokers.GetPublisherEnvVars(stack, broker, "payments")...) + + publisherEnvVars, err := brokers.GetPublisherEnvVars(stack, broker, "payments") + if err != nil { + return err + } + env = append(env, publisherEnvVars...) } serviceAccountName, err := settings.GetAWSServiceAccount(ctx, stack.Name) diff --git a/internal/resources/reconciliations/deployments.go b/internal/resources/reconciliations/deployments.go index d635d41cc..e1c171bf2 100644 --- a/internal/resources/reconciliations/deployments.go +++ b/internal/resources/reconciliations/deployments.go @@ -25,7 +25,11 @@ func createDeployment( imageConfiguration *registries.ImageConfiguration, ) error { env := make([]v1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, reconciliation), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, reconciliation) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } diff --git a/internal/resources/resourcereferences/init.go b/internal/resources/resourcereferences/init.go index e90e00227..9f77b8a04 100644 --- a/internal/resources/resourcereferences/init.go +++ b/internal/resources/resourcereferences/init.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" collectionutils "github.com/formancehq/go-libs/v5/pkg/types/collections" @@ -42,14 +43,16 @@ func init() { func watchResource[T client.Object](ctx core.Context, object T) []reconcile.Request { ret := make([]reconcile.Request, 0) + gvk, err := apiutil.GVKForObject(&v1beta1.ResourceReference{}, ctx.GetScheme()) + if err != nil { + log.FromContext(ctx).Error(err, "resolving ResourceReference kind, dropping event") + return ret + } + apiVersion, kind := gvk.ToAPIVersionAndKind() + // Watch resources created by the ResourceReference var resourceReference string for _, reference := range object.GetOwnerReferences() { - gvk, err := apiutil.GVKForObject(&v1beta1.ResourceReference{}, ctx.GetScheme()) - if err != nil { - panic(err) - } - apiVersion, kind := gvk.ToAPIVersionAndKind() if reference.Kind == kind && reference.APIVersion == apiVersion { resourceReference = reference.Name break @@ -158,7 +161,7 @@ func Reconcile(ctx core.Context, stack *v1beta1.Stack, req *v1beta1.ResourceRefe } if err := unstructured.SetNestedMap(content, annotations, "metadata", "annotations"); err != nil { - panic(err) + return errors.Wrap(err, "setting annotations on replicated resource") } hasOwnerReference, err := core.HasOwnerReference(ctx, req, newResource) diff --git a/internal/resources/settings/helpers.go b/internal/resources/settings/helpers.go index ddb3593eb..e15dc9432 100644 --- a/internal/resources/settings/helpers.go +++ b/internal/resources/settings/helpers.go @@ -384,7 +384,7 @@ func GetAs[T any](ctx core.Context, stack string, keys ...string) (*T, error) { data, err := json.Marshal(m) if err != nil { - panic(err) + return nil, err } if err := json.Unmarshal(data, &ret); err != nil { diff --git a/internal/resources/stacks/init.go b/internal/resources/stacks/init.go index 94fa0a35b..b7fb0f553 100644 --- a/internal/resources/stacks/init.go +++ b/internal/resources/stacks/init.go @@ -86,7 +86,7 @@ func setModulesCondition(ctx Context, stack *v1beta1.Stack) error { return errors.New("multiple modules found") } - func() { + if err := func() error { condition := v1beta1.NewCondition(ModuleReconciliation, stack.Generation). SetReason(gvk.Kind) defer func() { @@ -103,29 +103,33 @@ func setModulesCondition(ctx Context, stack *v1beta1.Stack) error { module := AnyModule{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(l.Items[0].UnstructuredContent(), &module); err != nil { - panic(err) + condition.SetStatus(metav1.ConditionFalse).SetMessage("Unable to read module status") + return errors.Wrapf(err, "converting module %s to structured object", gvk.Kind) } stackReconcileCondition := module.Status.Conditions.Get("ReconciledWithStack") if stackReconcileCondition == nil { condition.SetStatus(metav1.ConditionFalse).SetMessage("Module not yet reconciled") - return + return nil } if stackReconcileCondition.Status != metav1.ConditionTrue { condition.SetStatus(metav1.ConditionFalse).SetMessage("Module not declared as reconciled for stack") - return + return nil } if stackReconcileCondition.Reason == "Spec" && stack.MustSkip() { condition.SetStatus(metav1.ConditionFalse).SetMessage("Module should be skipped but is not") - return + return nil } if stackReconcileCondition.Reason == "Skipped" && !stack.MustSkip() { condition.SetStatus(metav1.ConditionFalse).SetMessage("Module is skipped but should not") - return + return nil } condition.SetMessage("All checks passed") - }() + return nil + }(); err != nil { + return err + } } modules := make([]string, 0) diff --git a/internal/resources/stargates/deployment.go b/internal/resources/stargates/deployment.go index fb16a5b3d..dd1945256 100644 --- a/internal/resources/stargates/deployment.go +++ b/internal/resources/stargates/deployment.go @@ -17,7 +17,11 @@ func createDeployment(ctx core.Context, stack *v1beta1.Stack, stargate *v1beta1. env := make([]v1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, stargate), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, stargate) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } diff --git a/internal/resources/transactionplane/deployments.go b/internal/resources/transactionplane/deployments.go index c554a5ad4..ae2d9e94b 100644 --- a/internal/resources/transactionplane/deployments.go +++ b/internal/resources/transactionplane/deployments.go @@ -50,7 +50,11 @@ func commonEnvVars( ) ([]corev1.EnvVar, error) { env := make([]corev1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, LowerCamelCaseKind(ctx, t), " ") + serviceName, err := LowerCamelCaseKind(ctx, t) + if err != nil { + return nil, err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return nil, err } @@ -98,7 +102,12 @@ func commonEnvVars( return nil, err } env = append(env, brokerEnvVars...) - env = append(env, brokers.GetPublisherEnvVars(stack, broker, "transactionplane")...) + + publisherEnvVars, err := brokers.GetPublisherEnvVars(stack, broker, "transactionplane") + if err != nil { + return nil, err + } + env = append(env, publisherEnvVars...) return env, nil } diff --git a/internal/resources/wallets/deployment.go b/internal/resources/wallets/deployment.go index 69ec69398..d20dbaead 100644 --- a/internal/resources/wallets/deployment.go +++ b/internal/resources/wallets/deployment.go @@ -18,7 +18,11 @@ import ( func createDeployment(ctx core.Context, stack *v1beta1.Stack, wallets *v1beta1.Wallets, authClient *v1beta1.AuthClient, version string) error { env := make([]v1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, wallets), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, wallets) + if err != nil { + return err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return err } diff --git a/internal/resources/webhooks/deployment.go b/internal/resources/webhooks/deployment.go index eea68bc15..2d8a209ff 100644 --- a/internal/resources/webhooks/deployment.go +++ b/internal/resources/webhooks/deployment.go @@ -28,7 +28,11 @@ func deploymentEnvVars(ctx core.Context, stack *v1beta1.Stack, webhooks *v1beta1 } env := make([]v1.EnvVar, 0) - otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, core.LowerCamelCaseKind(ctx, webhooks), " ") + serviceName, err := core.LowerCamelCaseKind(ctx, webhooks) + if err != nil { + return nil, err + } + otlpEnv, err := settings.GetOTELEnvVars(ctx, stack.Name, serviceName, " ") if err != nil { return nil, err } diff --git a/pkg/client/formance.com/v1beta1/client.go b/pkg/client/formance.com/v1beta1/client.go index b0b43011b..8eb8f83f5 100644 --- a/pkg/client/formance.com/v1beta1/client.go +++ b/pkg/client/formance.com/v1beta1/client.go @@ -1,6 +1,7 @@ package v1beta1 import ( + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -8,9 +9,7 @@ import ( ) func init() { - if err := v1beta1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } + utilruntime.Must(v1beta1.AddToScheme(scheme.Scheme)) } type Client struct { diff --git a/tools/kubectl-stacks/disable.go b/tools/kubectl-stacks/disable.go index bbeeda1d3..1f469aaba 100644 --- a/tools/kubectl-stacks/disable.go +++ b/tools/kubectl-stacks/disable.go @@ -33,7 +33,7 @@ func disable(cmd *cobra.Command, client *rest.RESTClient, name string) error { }, }) if err != nil { - panic(err) + return err } return client.Patch(types.MergePatchType). diff --git a/tools/kubectl-stacks/enable.go b/tools/kubectl-stacks/enable.go index 5ee2b6e17..7efd74943 100644 --- a/tools/kubectl-stacks/enable.go +++ b/tools/kubectl-stacks/enable.go @@ -33,7 +33,7 @@ func enable(cmd *cobra.Command, client *rest.RESTClient, name string) error { }, }) if err != nil { - panic(err) + return err } return client.Patch(types.MergePatchType). diff --git a/tools/kubectl-stacks/lock.go b/tools/kubectl-stacks/lock.go index 47a15a7b2..f17ec1073 100644 --- a/tools/kubectl-stacks/lock.go +++ b/tools/kubectl-stacks/lock.go @@ -61,7 +61,7 @@ func lockStack(cmd *cobra.Command, client *rest.RESTClient, name string) error { }, }) if err != nil { - panic(err) + return err } return client.Patch(types.MergePatchType). diff --git a/tools/kubectl-stacks/main.go b/tools/kubectl-stacks/main.go index a078af91f..abb79f772 100644 --- a/tools/kubectl-stacks/main.go +++ b/tools/kubectl-stacks/main.go @@ -51,15 +51,14 @@ func main() { flags := pflag.NewFlagSet("kubectl-stacks", pflag.ExitOnError) pflag.CommandLine = flags - root := NewRootCommand() - if err := root.Execute(); err != nil { + if err := v1beta1.AddToScheme(scheme.Scheme); err != nil { _, _ = fmt.Fprintln(os.Stderr, err) os.Exit(1) } -} -func init() { - if err := v1beta1.AddToScheme(scheme.Scheme); err != nil { - panic(err) + root := NewRootCommand() + if err := root.Execute(); err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } diff --git a/tools/kubectl-stacks/set-debug.go b/tools/kubectl-stacks/set-debug.go index c0869afdd..7cf4ecffb 100644 --- a/tools/kubectl-stacks/set-debug.go +++ b/tools/kubectl-stacks/set-debug.go @@ -33,7 +33,7 @@ func setDebug(cmd *cobra.Command, client *rest.RESTClient, name string, b bool) }, }) if err != nil { - panic(err) + return err } return client.Patch(types.MergePatchType). diff --git a/tools/kubectl-stacks/unlock.go b/tools/kubectl-stacks/unlock.go index 042f4d16e..2f093845a 100644 --- a/tools/kubectl-stacks/unlock.go +++ b/tools/kubectl-stacks/unlock.go @@ -75,7 +75,7 @@ func unlockStack(cmd *cobra.Command, client *rest.RESTClient, name string) error }, }) if err != nil { - panic(err) + return err } return client.Patch(types.MergePatchType). diff --git a/tools/kubectl-stacks/upgrade.go b/tools/kubectl-stacks/upgrade.go index 12f77adb7..2543b3fb6 100644 --- a/tools/kubectl-stacks/upgrade.go +++ b/tools/kubectl-stacks/upgrade.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "fmt" "time" "github.com/pterm/pterm" @@ -34,14 +36,14 @@ func NewUpgradeCommand(configFlags *genericclioptions.ConfigFlags) *cobra.Comman return ret } -func upgrade(cmd *cobra.Command, client *rest.RESTClient) error { +func upgrade(cmd *cobra.Command, client *rest.RESTClient) (returnErr error) { stackList, err := lockAllStacks(cmd, client) if err != nil { return err } defer func() { if err := unlockAllStacks(cmd, client); err != nil { - panic(err) + returnErr = errors.Join(returnErr, fmt.Errorf("unlocking stacks: %w", err)) } }() for _, stack := range stackList.Items {