Skip to content

Commit 446244a

Browse files
authored
Support custom envs for the app push command (#2635)
1 parent a2fb9ec commit 446244a

File tree

14 files changed

+1125
-25
lines changed

14 files changed

+1125
-25
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/google/go-containerregistry v0.20.6
1212
github.com/itchyny/gojq v0.12.17
1313
github.com/jedib0t/go-pretty/v6 v6.6.8
14+
github.com/joho/godotenv v1.5.1
1415
github.com/kyma-project/api-gateway v0.0.0-20250814120053-7d617def4106
1516
github.com/moby/go-archive v0.1.0
1617
github.com/moby/term v0.5.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i
277277
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
278278
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
279279
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
280+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
281+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
280282
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
281283
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
282284
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=

internal/cmd/app/push.go

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/kyma-project/cli.v3/internal/clierror"
1010
"github.com/kyma-project/cli.v3/internal/cmdcommon"
11+
"github.com/kyma-project/cli.v3/internal/cmdcommon/env"
1112
"github.com/kyma-project/cli.v3/internal/cmdcommon/types"
1213
"github.com/kyma-project/cli.v3/internal/dockerfile"
1314
"github.com/kyma-project/cli.v3/internal/flags"
@@ -31,6 +32,10 @@ type appPushConfig struct {
3132
packAppPath string
3233
containerPort types.NullableInt64
3334
istioInject types.NullableBool
35+
envs types.Map
36+
fileEnvs types.SourcedEnvArray
37+
configmapEnvs types.SourcedEnvArray
38+
secretEnvs types.SourcedEnvArray
3439
expose bool
3540
mountSecrets []string
3641
mountConfigmaps []string
@@ -40,6 +45,7 @@ type appPushConfig struct {
4045
func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command {
4146
config := appPushConfig{
4247
KymaConfig: kymaConfig,
48+
envs: types.Map{Values: map[string]interface{}{}},
4349
}
4450

4551
cmd := &cobra.Command{
@@ -67,6 +73,10 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command {
6773
// common flags
6874
cmd.Flags().StringVar(&config.name, "name", "", "Name of the app")
6975
cmd.Flags().BoolVarP(&config.quiet, "quiet", "q", false, "Suppresses non-essential output (prints only the URL of the pushed app, if exposed)")
76+
cmd.Flags().Var(&config.envs, "env", "Environment variables for the app in format KEY=VALUE")
77+
cmd.Flags().Var(&config.fileEnvs, "env-from-file", "Environment variables for the app loaded from a file in format KEY=PATH:KEY for single key or PATH[:PREFIX] to fetch all keys")
78+
cmd.Flags().Var(&config.configmapEnvs, "env-from-configmap", "Environment variables for the app loaded from a ConfigMap in format KEY=PATH:KEY for single key or PATH[:PREFIX] to fetch all keys")
79+
cmd.Flags().Var(&config.secretEnvs, "env-from-secret", "Environment variables for the app loaded from a Secret in format KEY=PATH:KEY for single key or PATH[:PREFIX] to fetch all keys")
7080

7181
// image flags
7282
cmd.Flags().StringVar(&config.image, "image", "", "Name of the image to deploy")
@@ -163,22 +173,14 @@ func runAppPush(cfg *appPushConfig) clierror.Error {
163173

164174
fmt.Fprintf(writer, "\nCreating deployment %s/%s\n", cfg.namespace, cfg.name)
165175

166-
err := resources.CreateDeployment(cfg.Ctx, client, resources.CreateDeploymentOpts{
167-
Name: cfg.name,
168-
Namespace: cfg.namespace,
169-
Image: image,
170-
ImagePullSecret: imagePullSecret,
171-
InjectIstio: cfg.istioInject,
172-
SecretMounts: cfg.mountSecrets,
173-
ConfigmapMounts: cfg.mountConfigmaps,
174-
})
175-
if err != nil {
176-
return clierror.Wrap(err, clierror.New("failed to create deployment"))
176+
clierr = createDeployment(cfg, client, image, imagePullSecret)
177+
if clierr != nil {
178+
return clierr
177179
}
178180

179181
if cfg.containerPort.Value != nil {
180182
fmt.Fprintf(writer, "\nCreating service %s/%s\n", cfg.namespace, cfg.name)
181-
err = resources.CreateService(cfg.Ctx, client, cfg.name, cfg.namespace, int32(*cfg.containerPort.Value))
183+
err := resources.CreateService(cfg.Ctx, client, cfg.name, cfg.namespace, int32(*cfg.containerPort.Value))
182184
if err != nil {
183185
return clierror.Wrap(err, clierror.New("failed to create service"))
184186
}
@@ -188,7 +190,7 @@ func runAppPush(cfg *appPushConfig) clierror.Error {
188190
fmt.Fprintf(writer, "\nCreating API Rule %s/%s\n", cfg.namespace, cfg.name)
189191
url := fmt.Sprintf("%s.<CLUSTER_DOMAIN>", cfg.name)
190192

191-
err = resources.CreateAPIRule(cfg.Ctx, client.RootlessDynamic(), cfg.name, cfg.namespace, cfg.name, uint32(*cfg.containerPort.Value))
193+
err := resources.CreateAPIRule(cfg.Ctx, client.RootlessDynamic(), cfg.name, cfg.namespace, cfg.name, uint32(*cfg.containerPort.Value))
192194
if err != nil {
193195
return clierror.Wrap(err, clierror.New("failed to create API Rule resource", "Make sure API Gateway module is installed", "Make sure APIRule CRD is available in v2 version"))
194196
}
@@ -216,6 +218,42 @@ func runAppPush(cfg *appPushConfig) clierror.Error {
216218
return nil
217219
}
218220

221+
func createDeployment(cfg *appPushConfig, client kube.Client, image, imagePullSecret string) clierror.Error {
222+
fileEnvs, err := env.BuildEnvsFromFile(cfg.fileEnvs)
223+
if err != nil {
224+
return clierror.Wrap(err, clierror.New("failed to build envs from file"))
225+
}
226+
227+
secretEnvs, err := env.BuildEnvsFromSecret(cfg.Ctx, client, cfg.namespace, cfg.secretEnvs)
228+
if err != nil {
229+
return clierror.Wrap(err, clierror.New("failed to build envs from secret"))
230+
}
231+
232+
configmapEnvs, err := env.BuildEnvsFromConfigmap(cfg.Ctx, client, cfg.namespace, cfg.configmapEnvs)
233+
if err != nil {
234+
return clierror.Wrap(err, clierror.New("failed to build envs from configmap"))
235+
}
236+
237+
envs := append(fileEnvs, secretEnvs...)
238+
envs = append(envs, configmapEnvs...)
239+
240+
err = resources.CreateDeployment(cfg.Ctx, client, resources.CreateDeploymentOpts{
241+
Name: cfg.name,
242+
Namespace: cfg.namespace,
243+
Image: image,
244+
ImagePullSecret: imagePullSecret,
245+
InjectIstio: cfg.istioInject,
246+
SecretMounts: cfg.mountSecrets,
247+
ConfigmapMounts: cfg.mountConfigmaps,
248+
Envs: envs,
249+
})
250+
if err != nil {
251+
return clierror.Wrap(err, clierror.New("failed to create deployment"))
252+
}
253+
254+
return nil
255+
}
256+
219257
func buildAndImportImage(client kube.Client, cfg *appPushConfig, registryConfig *registry.InternalRegistryConfig) (string, clierror.Error) {
220258
fmt.Print("Building image\n\n")
221259
imageName, err := buildImage(cfg)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package env
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/kyma-project/cli.v3/internal/cmdcommon/types"
8+
"github.com/kyma-project/cli.v3/internal/kube"
9+
corev1 "k8s.io/api/core/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
)
12+
13+
func BuildEnvsFromConfigmap(ctx context.Context, client kube.Client, namespace string, envs types.SourcedEnvArray) ([]corev1.EnvVar, error) {
14+
var result []corev1.EnvVar
15+
for _, e := range envs.Values {
16+
if e.Location == "" {
17+
return nil, fmt.Errorf("missing configmap name in env: '%s'", e.String())
18+
}
19+
20+
if e.Name != "" {
21+
// single env var from configmap
22+
if e.LocationKey == "" {
23+
return nil, fmt.Errorf("missing configmap key in env: '%s'", e.String())
24+
}
25+
26+
result = append(result, corev1.EnvVar{
27+
Name: e.Name,
28+
ValueFrom: &corev1.EnvVarSource{
29+
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
30+
Key: e.LocationKey,
31+
LocalObjectReference: corev1.LocalObjectReference{
32+
Name: e.Location,
33+
},
34+
},
35+
},
36+
})
37+
continue
38+
}
39+
40+
// multi env vars from configmap
41+
data, err := getConfigmapData(ctx, client, namespace, e.Location)
42+
if err != nil {
43+
return nil, fmt.Errorf("while reading configmap '%s': %w", e.Location, err)
44+
}
45+
46+
cmEnvs := buildConfigmapAllKeyEnvs(data, e.Location, e.LocationKeysPrefix)
47+
result = append(result, cmEnvs...)
48+
}
49+
50+
return result, nil
51+
}
52+
53+
func getConfigmapData(ctx context.Context, client kube.Client, namespace, name string) (map[string]string, error) {
54+
cm, err := client.Static().CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{})
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
return cm.Data, nil
60+
}
61+
62+
func buildConfigmapAllKeyEnvs(data map[string]string, resName string, prefix string) []corev1.EnvVar {
63+
var result []corev1.EnvVar
64+
for k := range data {
65+
result = append(result, corev1.EnvVar{
66+
Name: fmt.Sprintf("%s%s", prefix, k),
67+
ValueFrom: &corev1.EnvVarSource{
68+
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
69+
Key: k,
70+
LocalObjectReference: corev1.LocalObjectReference{
71+
Name: resName,
72+
},
73+
},
74+
},
75+
})
76+
}
77+
78+
return result
79+
}

0 commit comments

Comments
 (0)