Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
30 changes: 25 additions & 5 deletions api/util/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,28 @@ func VerifiedAppHosts(app *apps.Application) []string {
return verifiedHosts
}

func EnvVarsFromMap(env map[string]string) apps.EnvVars {
type EnvVarModifier func(envVar *apps.EnvVar)

func EnvVarsFromMap(env map[string]string, options ...EnvVarModifier) apps.EnvVars {
vars := apps.EnvVars{}
for k, v := range env {
vars = append(vars, apps.EnvVar{Name: k, Value: v})
envVar := apps.EnvVar{Name: k, Value: v}
for _, opt := range options {
opt(&envVar)
}
vars = append(vars, envVar)
}
return vars
}

func UpdateEnvVars(oldEnvs []apps.EnvVar, newEnvs map[string]string, toDelete []string) apps.EnvVars {
if len(newEnvs) == 0 && len(toDelete) == 0 {
func Sensitive() EnvVarModifier {
return func(envVar *apps.EnvVar) {
envVar.Sensitive = ptr.To(true)
}
}

func UpdateEnvVars(oldEnvs []apps.EnvVar, newEnvs, sensitiveEnvs map[string]string, toDelete []string) apps.EnvVars {
if len(newEnvs) == 0 && len(sensitiveEnvs) == 0 && len(toDelete) == 0 {
return oldEnvs
}

Expand All @@ -95,6 +107,10 @@ func UpdateEnvVars(oldEnvs []apps.EnvVar, newEnvs map[string]string, toDelete []
for _, v := range new {
envMap[v.Name] = v
}
sensitive := EnvVarsFromMap(sensitiveEnvs, Sensitive())
for _, v := range sensitive {
envMap[v.Name] = v
}

for _, v := range toDelete {
delete(envMap, v)
Expand Down Expand Up @@ -123,7 +139,11 @@ func EnvVarToString(envs apps.EnvVars) string {

var keyValuePairs []string
for _, env := range envs {
keyValuePairs = append(keyValuePairs, fmt.Sprintf("%v=%v", env.Name, env.Value))
if env.Sensitive != nil && *env.Sensitive {
keyValuePairs = append(keyValuePairs, fmt.Sprintf("%v=*****", env.Name))
} else {
keyValuePairs = append(keyValuePairs, fmt.Sprintf("%v=%v", env.Name, env.Value))
}
}

return strings.Join(keyValuePairs, ";")
Expand Down
2 changes: 1 addition & 1 deletion api/util/apps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestEnvUpdate(t *testing.T) {
}
up := map[string]string{"old2": "val2"}
del := []string{"old3"}
new := UpdateEnvVars(old, up, del)
new := UpdateEnvVars(old, up, nil, del)
expected := apps.EnvVars{
{
Name: "old1",
Expand Down
15 changes: 12 additions & 3 deletions create/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"slices"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -42,7 +43,9 @@ type applicationCmd struct {
Hosts []string `help:"Host names where the app can be accessed. If empty, the app will just be accessible on a generated host name on the deploio.app domain."`
BasicAuth *bool `help:"Enable/Disable basic authentication for the app (defaults to ${app_default_basic_auth})." placeholder:"${app_default_basic_auth}"`
Env map[string]string `help:"Environment variables which are passed to the app at runtime."`
SensitiveEnv map[string]string `help:"Sensitive environment variables which are passed to the app at runtime."`
BuildEnv map[string]string `help:"Environment variables which are passed to the app build process."`
SensitiveBuildEnv map[string]string `help:"Sensitive environment variables which are passed to the app build process."`
DeployJob deployJob `embed:"" prefix:"deploy-job-"`
WorkerJob workerJob `embed:"" prefix:"worker-job-"`
ScheduledJob scheduledJob `embed:"" prefix:"scheduled-job-"`
Expand Down Expand Up @@ -273,6 +276,13 @@ func spinnerMessage(msg, icon string, sleepTime time.Duration) error {
return spinner.Stop()
}

func combineEnvVars(plain, sensitive map[string]string) apps.EnvVars {
return slices.Concat(
util.EnvVarsFromMap(plain),
util.EnvVarsFromMap(sensitive, util.Sensitive()),
)
}

func (app *applicationCmd) config() apps.Config {
var deployJob *apps.DeployJob

Expand All @@ -288,10 +298,9 @@ func (app *applicationCmd) config() apps.Config {
},
}
}

config := apps.Config{
EnableBasicAuth: app.BasicAuth,
Env: util.EnvVarsFromMap(app.Env),
Env: combineEnvVars(app.Env, app.SensitiveEnv),
DeployJob: deployJob,
}

Expand Down Expand Up @@ -358,7 +367,7 @@ func (app *applicationCmd) newApplication(project string) *apps.Application {
},
Hosts: app.Hosts,
Config: app.config(),
BuildEnv: util.EnvVarsFromMap(app.BuildEnv),
BuildEnv: combineEnvVars(app.BuildEnv, app.SensitiveBuildEnv),
DockerfileBuild: apps.DockerfileBuild{
Enabled: app.DockerfileBuild.Enabled,
DockerfilePath: app.DockerfileBuild.Path,
Expand Down
28 changes: 28 additions & 0 deletions create/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,34 @@ func TestApplication(t *testing.T) {
assert.Nil(t, app.Spec.ForProvider.Git.Auth)
},
},
"with sensitive env": {
cmd: applicationCmd{
resourceCmd: resourceCmd{
Name: "sensitive-env-test",
},
Git: gitConfig{
URL: "https://github.com/ninech/doesnotexist.git",
SubPath: "/my/app",
Revision: "superbug",
},
SensitiveEnv: map[string]string{"secret": "orange"},
SensitiveBuildEnv: map[string]string{"build_secret": "banana"},
SkipRepoAccessCheck: true,
},
checkApp: func(t *testing.T, cmd applicationCmd, app *apps.Application) {
env := util.EnvVarByName(app.Spec.ForProvider.Config.Env, "secret")
require.NotNil(t, env)
require.NotNil(t, env.Sensitive)
assert.True(t, *env.Sensitive)
assert.Equal(t, "orange", env.Value)

buildEnv := util.EnvVarByName(app.Spec.ForProvider.BuildEnv, "build_secret")
require.NotNil(t, buildEnv)
require.NotNil(t, buildEnv.Sensitive)
assert.True(t, *buildEnv.Sensitive)
assert.Equal(t, "banana", buildEnv.Value)
},
},
}

for name, tc := range cases {
Expand Down
52 changes: 52 additions & 0 deletions get/project_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,58 @@ func TestProjectConfigs(t *testing.T) {
project: "ns-3",
expectExactMessage: ptr.To("no ProjectConfigs found in project ns-3\n"),
},
"sensitive env var is masked": {
get: &Cmd{
output: output{
Format: full,
},
},
project: "ns-4",
createdConfigs: []client.Object{
&apps.ProjectConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-4",
Namespace: "ns-4",
},
Spec: apps.ProjectConfigSpec{
ForProvider: apps.ProjectConfigParameters{
Config: apps.Config{
Env: util.EnvVarsFromMap(map[string]string{"poo": "orange"}, util.Sensitive()),
},
},
},
},
},
expectExactMessage: ptr.To(
"PROJECT NAME SIZE REPLICAS PORT ENVIRONMENT_VARIABLES BASIC_AUTH DEPLOY_JOB AGE\nns-4 ns-4 poo=***** false <none> 292y\n",
),
},
"non-sensitive env var is shown": {
get: &Cmd{
output: output{
Format: full,
},
},
project: "ns-5",
createdConfigs: []client.Object{
&apps.ProjectConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-5",
Namespace: "ns-5",
},
Spec: apps.ProjectConfigSpec{
ForProvider: apps.ProjectConfigParameters{
Config: apps.Config{
Env: util.EnvVarsFromMap(map[string]string{"goo": "banana"}),
},
},
},
},
},
expectExactMessage: ptr.To(
"PROJECT NAME SIZE REPLICAS PORT ENVIRONMENT_VARIABLES BASIC_AUTH DEPLOY_JOB AGE\nns-5 ns-5 goo=banana false <none> 292y\n",
),
},
}

for name, tc := range cases {
Expand Down
49 changes: 37 additions & 12 deletions update/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ type applicationCmd struct {
BasicAuth *bool `help:"Enable/Disable basic authentication for the application."`
ChangeBasicAuthPassword *bool `help:"Generate a new basic auth password."`
Env map[string]string `help:"Environment variables which are passed to the app at runtime."`
SensitiveEnv map[string]string `help:"Sensitive environment variables which are passed to the app at runtime."`
DeleteEnv *[]string `help:"Runtime environment variables names which are to be deleted."`
BuildEnv map[string]string `help:"Environment variables names which are passed to the app build process."`
DeleteBuildEnv *[]string `help:"Build environment variables which are to be deleted."`

BuildEnv map[string]string `help:"Environment variables names which are passed to the app build process."`
SensitiveBuildEnv map[string]string `help:"Sensitive environment variables names which are passed to the app build process."`
DeleteBuildEnv *[]string `help:"Build environment variables which are to be deleted."`

// DeployJob, ScheduledJob and WorkerJob are embedded pointers to
// structs. Due to the usage of kong these pointers will never be `nil`.
// So checking for `nil` values can not be used to find out if some of
Expand Down Expand Up @@ -263,32 +267,53 @@ func (cmd *applicationCmd) applyUpdates(app *apps.Application) {
app.Spec.ForProvider.Language = apps.Language(*cmd.Language)
}

runtimeEnv := make(map[string]string)
if cmd.Env != nil {
runtimeEnv = cmd.Env
runtimeEnv := cmd.Env
if runtimeEnv == nil {
runtimeEnv = make(map[string]string)
}

sensitiveRuntimeEnv := cmd.SensitiveEnv
if sensitiveRuntimeEnv == nil {
sensitiveRuntimeEnv = make(map[string]string)
}

if cmd.RetryRelease != nil && *cmd.RetryRelease {
runtimeEnv[ReleaseTrigger] = triggerTimestamp()
}

var delEnv []string
if cmd.DeleteEnv != nil {
delEnv = *cmd.DeleteEnv
}
app.Spec.ForProvider.Config.Env = util.UpdateEnvVars(app.Spec.ForProvider.Config.Env, runtimeEnv, delEnv)

buildEnv := make(map[string]string)
if cmd.BuildEnv != nil {
buildEnv = cmd.BuildEnv
app.Spec.ForProvider.Config.Env = util.UpdateEnvVars(app.Spec.ForProvider.Config.Env, runtimeEnv, sensitiveRuntimeEnv, delEnv)

// build env vars
buildEnv := cmd.BuildEnv
if buildEnv == nil {
buildEnv = make(map[string]string)
}

sensitiveBuildEnv := cmd.SensitiveBuildEnv
if sensitiveBuildEnv == nil {
sensitiveBuildEnv = make(map[string]string)
}

if cmd.RetryBuild != nil && *cmd.RetryBuild {
buildEnv[BuildTrigger] = triggerTimestamp()
}
var buildDelEnv []string

var delBuildEnv []string
if cmd.DeleteBuildEnv != nil {
buildDelEnv = *cmd.DeleteBuildEnv
delBuildEnv = *cmd.DeleteBuildEnv
}
app.Spec.ForProvider.BuildEnv = util.UpdateEnvVars(app.Spec.ForProvider.BuildEnv, buildEnv, buildDelEnv)

app.Spec.ForProvider.BuildEnv = util.UpdateEnvVars(
app.Spec.ForProvider.BuildEnv,
buildEnv,
sensitiveBuildEnv,
delBuildEnv,
)
if cmd.Pause != nil && *cmd.Pause {
app.Spec.ForProvider.Paused = *cmd.Pause
}
Expand Down
52 changes: 41 additions & 11 deletions update/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestApplication(t *testing.T) {
Size: initialSize,
Replicas: ptr.To(int32(1)),
Port: ptr.To(int32(1337)),
Env: util.EnvVarsFromMap(map[string]string{"foo": "bar"}),
Env: util.EnvVarsFromMap(map[string]string{"foo": "bar", "poo": "blue"}),
EnableBasicAuth: ptr.To(false),
DeployJob: &apps.DeployJob{
Job: apps.Job{
Expand Down Expand Up @@ -114,13 +114,14 @@ func TestApplication(t *testing.T) {
SubPath: ptr.To("new/path"),
Revision: ptr.To("some-change"),
},
Size: ptr.To("newsize"),
Port: ptr.To(int32(1234)),
Replicas: ptr.To(int32(999)),
Hosts: &[]string{"one.example.org", "two.example.org"},
Env: map[string]string{"bar": "zoo"},
BuildEnv: map[string]string{"BP_GO_TARGETS": "./cmd/web-server"},
BasicAuth: ptr.To(true),
Size: ptr.To("newsize"),
Port: ptr.To(int32(1234)),
Replicas: ptr.To(int32(999)),
Hosts: &[]string{"one.example.org", "two.example.org"},
Env: map[string]string{"bar": "zoo"},
SensitiveEnv: map[string]string{"secret": "orange"},
BuildEnv: map[string]string{"BP_GO_TARGETS": "./cmd/web-server"},
BasicAuth: ptr.To(true),
DeployJob: &deployJob{
Command: ptr.To("exit 0"), Name: ptr.To("exit"),
Retries: ptr.To(int32(1)), Timeout: ptr.To(time.Minute * 5),
Expand All @@ -136,8 +137,15 @@ func TestApplication(t *testing.T) {
assert.Equal(t, *cmd.Replicas, *updated.Spec.ForProvider.Config.Replicas)
assert.Equal(t, *cmd.BasicAuth, *updated.Spec.ForProvider.Config.EnableBasicAuth)
assert.Equal(t, *cmd.Hosts, updated.Spec.ForProvider.Hosts)
assert.Equal(t, util.UpdateEnvVars(existingApp.Spec.ForProvider.Config.Env, cmd.Env, nil), updated.Spec.ForProvider.Config.Env)
assert.Equal(t, util.UpdateEnvVars(existingApp.Spec.ForProvider.BuildEnv, cmd.BuildEnv, nil), updated.Spec.ForProvider.BuildEnv)
assert.Equal(t, util.UpdateEnvVars(existingApp.Spec.ForProvider.Config.Env, cmd.Env, cmd.SensitiveEnv, nil), updated.Spec.ForProvider.Config.Env)
assert.Equal(t, util.UpdateEnvVars(existingApp.Spec.ForProvider.BuildEnv, cmd.BuildEnv, cmd.SensitiveBuildEnv, nil), updated.Spec.ForProvider.BuildEnv)

secretKeyEnv := util.EnvVarByName(updated.Spec.ForProvider.Config.Env, "secret")
require.NotNil(t, secretKeyEnv, "secret environment variable should exist")
require.NotNil(t, secretKeyEnv.Sensitive)
assert.Equal(t, "orange", secretKeyEnv.Value)
assert.True(t, *secretKeyEnv.Sensitive)

assert.Equal(t, *cmd.DeployJob.Command, updated.Spec.ForProvider.Config.DeployJob.Command)
assert.Equal(t, *cmd.DeployJob.Name, updated.Spec.ForProvider.Config.DeployJob.Name)
assert.Equal(t, *cmd.DeployJob.Timeout, updated.Spec.ForProvider.Config.DeployJob.Timeout.Duration)
Expand All @@ -156,7 +164,13 @@ func TestApplication(t *testing.T) {
DeleteEnv: &[]string{"foo"},
},
checkApp: func(t *testing.T, cmd applicationCmd, orig, updated *apps.Application) {
assert.Empty(t, updated.Spec.ForProvider.Config.Env)
foundFoo := false
for _, env := range updated.Spec.ForProvider.Config.Env {
if env.Name == "foo" {
foundFoo = true
}
}
assert.False(t, foundFoo)
assert.NotEmpty(t, updated.Spec.ForProvider.BuildEnv)
},
},
Expand Down Expand Up @@ -187,6 +201,22 @@ func TestApplication(t *testing.T) {
assert.NotEmpty(t, updated.Spec.ForProvider.Config.Env)
},
},
"update variable from normal/sensitive": {
orig: existingApp,
cmd: applicationCmd{
resourceCmd: resourceCmd{
Name: existingApp.Name,
},
SensitiveEnv: map[string]string{"poo": "blue"},
},
checkApp: func(t *testing.T, cmd applicationCmd, orig, updated *apps.Application) {
sensitivePoo := util.EnvVarByName(updated.Spec.ForProvider.Config.Env, "poo")
require.NotNil(t, sensitivePoo)
require.NotNil(t, sensitivePoo.Sensitive)
assert.True(t, *sensitivePoo.Sensitive)
},
},

"change basic auth password": {
orig: existingApp,
cmd: applicationCmd{
Expand Down