@@ -10,6 +10,10 @@ import (
1010 "github.com/replicatedhq/embedded-cluster/pkg/netutils"
1111 "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
1212 kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
13+ corev1 "k8s.io/api/core/v1"
14+ apierrors "k8s.io/apimachinery/pkg/api/errors"
15+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+ "sigs.k8s.io/controller-runtime/pkg/client"
1317 kyaml "sigs.k8s.io/yaml"
1418)
1519
@@ -29,6 +33,11 @@ func (m *appInstallManager) Install(ctx context.Context, configValues kotsv1beta
2933 return fmt .Errorf ("get kotsadm namespace: %w" , err )
3034 }
3135
36+ // Create or update secret with config values before installing
37+ if err := m .createConfigValuesSecret (ctx , configValues , kotsadmNamespace ); err != nil {
38+ return fmt .Errorf ("creating config values secret: %w" , err )
39+ }
40+
3241 ecDomains := utils .GetDomains (m .releaseData )
3342
3443 installOpts := kotscli.InstallOptions {
@@ -79,3 +88,75 @@ func (m *appInstallManager) createConfigValuesFile(configValues kotsv1beta1.Conf
7988
8089 return configValuesFile .Name (), nil
8190}
91+
92+ // createConfigValuesSecret creates or updates a Kubernetes secret with the config values.
93+ // TODO: Handle 1MB size limitation by storing large file data fields as pointers to other secrets
94+ // TODO: Consider maintaining history of config values for potential rollbacks
95+ func (m * appInstallManager ) createConfigValuesSecret (ctx context.Context , configValues kotsv1beta1.ConfigValues , namespace string ) error {
96+ // Get app slug and version from release data
97+ license := & kotsv1beta1.License {}
98+ if err := kyaml .Unmarshal (m .license , license ); err != nil {
99+ return fmt .Errorf ("parse license: %w" , err )
100+ }
101+
102+ if m .releaseData == nil || m .releaseData .ChannelRelease == nil {
103+ return fmt .Errorf ("release data is required for secret creation" )
104+ }
105+
106+ // Marshal config values to YAML
107+ data , err := kyaml .Marshal (configValues )
108+ if err != nil {
109+ return fmt .Errorf ("marshal config values: %w" , err )
110+ }
111+
112+ secretName := fmt .Sprintf ("%s-config-values" , license .Spec .AppSlug )
113+
114+ // Create secret object
115+ secret := & corev1.Secret {
116+ TypeMeta : metav1.TypeMeta {
117+ Kind : "Secret" ,
118+ APIVersion : "v1" ,
119+ },
120+ ObjectMeta : metav1.ObjectMeta {
121+ Name : secretName ,
122+ Namespace : namespace ,
123+ Labels : map [string ]string {
124+ "app.kubernetes.io/name" : license .Spec .AppSlug ,
125+ "app.kubernetes.io/version" : m .releaseData .ChannelRelease .VersionLabel ,
126+ "app.kubernetes.io/component" : "config" ,
127+ "app.kubernetes.io/part-of" : "embedded-cluster" ,
128+ "app.kubernetes.io/managed-by" : "embedded-cluster-installer" ,
129+ },
130+ },
131+ Type : corev1 .SecretTypeOpaque ,
132+ Data : map [string ][]byte {
133+ "config-values.yaml" : data ,
134+ },
135+ }
136+
137+ // Try to create the secret
138+ if err := m .kcli .Create (ctx , secret ); err != nil {
139+ if ! apierrors .IsAlreadyExists (err ) {
140+ return fmt .Errorf ("create config values secret: %w" , err )
141+ }
142+
143+ // Secret exists, get and update it
144+ existingSecret := & corev1.Secret {}
145+ if err := m .kcli .Get (ctx , client.ObjectKey {
146+ Name : secretName ,
147+ Namespace : namespace ,
148+ }, existingSecret ); err != nil {
149+ return fmt .Errorf ("get existing config values secret: %w" , err )
150+ }
151+
152+ // Update the existing secret's data and labels
153+ existingSecret .Data = secret .Data
154+ existingSecret .Labels = secret .Labels
155+
156+ if err := m .kcli .Update (ctx , existingSecret ); err != nil {
157+ return fmt .Errorf ("update config values secret: %w" , err )
158+ }
159+ }
160+
161+ return nil
162+ }
0 commit comments