Skip to content

Add more warning for service name replacement #541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions src/pkg/cli/compose/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ import (
func ConvertServices(ctx context.Context, c client.Client, serviceConfigs compose.Services, force BuildContext) ([]*defangv1.Service, error) {
// Create a regexp to detect private service names in environment variable values
var serviceNames []string
var nonReplaceServiceNames []string
for _, svccfg := range serviceConfigs {
if network(&svccfg) == defangv1.Network_PRIVATE && slices.ContainsFunc(svccfg.Ports, func(p compose.ServicePortConfig) bool {
return p.Mode == "host" // only private services with host ports get DNS names
}) {
serviceNames = append(serviceNames, regexp.QuoteMeta(svccfg.Name))
} else {
nonReplaceServiceNames = append(nonReplaceServiceNames, regexp.QuoteMeta(svccfg.Name))
}
}
var serviceNameRegex *regexp.Regexp
if len(serviceNames) > 0 {
serviceNameRegex = regexp.MustCompile(`\b(?:` + strings.Join(serviceNames, "|") + `)\b`)
}
var nonReplaceServiceNameRegex *regexp.Regexp
if len(nonReplaceServiceNames) > 0 {
nonReplaceServiceNameRegex = regexp.MustCompile(`\b(?:` + strings.Join(nonReplaceServiceNames, "|") + `)\b`)
}

// Preload the current config so we can detect which environment variables should be passed as "secrets"
config, err := c.ListConfig(ctx)
Expand Down Expand Up @@ -131,6 +138,9 @@ func ConvertServices(ctx context.Context, c client.Client, serviceConfigs compos
// Check if the environment variable is an existing config; if so, mark it as such
if _, ok := slices.BinarySearch(config.Names, key); ok {
term.Warnf("service %q: environment variable %q overridden by config", svccfg.Name, key)
if serviceNameRegex != nil && serviceNameRegex.MatchString(*value) {
term.Warnf("service %q: environment variable %q with value %q contains a service name needs to be normalized, but it will be repalced by an existing config. service names in a config will not be normalized.", svccfg.Name, key, *value)
}
envFromConfig = append(envFromConfig, key)
continue
}
Expand All @@ -143,6 +153,8 @@ func ConvertServices(ctx context.Context, c client.Client, serviceConfigs compos
})
if val != *value {
term.Warnf("service %q: service names were replaced in environment variable %q: %q", svccfg.Name, key, val)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"service names were fixed up"

} else if nonReplaceServiceNameRegex != nil && nonReplaceServiceNameRegex.MatchString(*value) {
term.Warnf("service %q: service names were NOT replaced in environment variable %q: %q, only service with port mode host will be replaced", svccfg.Name, key, val)
}
}
envs[key] = val
Expand Down
5 changes: 4 additions & 1 deletion src/pkg/cli/compose/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ func compare(actual []byte, goldenFile string) error {
}
return os.WriteFile(goldenFile, actual, 0644)
} else {
return diff(string(actual), string(golden))
if err := diff(string(actual), string(golden)); err != nil {
return fmt.Errorf("%s %w", goldenFile, err)
}
}
return nil
}

func diff(actualRaw, goldenRaw string) error {
Expand Down
28 changes: 27 additions & 1 deletion src/pkg/cli/compose/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
"strings"
"testing"

"github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/term"
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
)

func TestValidation(t *testing.T) {
func TestValidationAndConvert(t *testing.T) {
oldTerm := term.DefaultTerm
t.Cleanup(func() {
term.DefaultTerm = oldTerm
Expand All @@ -30,6 +32,14 @@ func TestValidation(t *testing.T) {
logs.WriteString(err.Error() + "\n")
}

mockClient := MockClient{
configs: []string{"CONFIG1", "CONFIG2"},
}
if _, err = ConvertServices(context.Background(), mockClient, proj.Services, BuildContextIgnore); err != nil {
t.Logf("Service conversion failed: %v", err)
logs.WriteString(err.Error() + "\n")
}

// The order of the services is not guaranteed, so we sort the logs before comparing
logLines := strings.Split(strings.Trim(logs.String(), "\n"), "\n")
slices.Sort(logLines)
Expand All @@ -41,3 +51,19 @@ func TestValidation(t *testing.T) {
}
})
}

type MockClient struct {
client.Client
configs []string
}

func (m MockClient) ListConfig(ctx context.Context) (*defangv1.Secrets, error) {
return &defangv1.Secrets{
Names: m.configs,
Project: "mock-project",
}, nil
}

func (m MockClient) ServiceDNS(name string) string {
return "mock-" + name
}
1 change: 1 addition & 0 deletions src/tests/alttestproj/compose.yaml.warnings
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Compressing build context for dfnx at .
3 changes: 2 additions & 1 deletion src/tests/emptyenv/compose.yaml.warnings
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
! service "emptyenv": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "emptyenv": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "emptyenv": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
* Compressing build context for emptyenv at .
13 changes: 13 additions & 0 deletions src/tests/fixupenv/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,16 @@ services:
environment:
- "API_URL=http://Mistral:8000"
- "SENSITIVE_DATA"
bad-service:
image: "somedb:latest"
ports:
- mode: ingress
target: 5432
use-bad-service:
image: "service:latest"
environment:
- "DB_URL=bad-service:5432"
env-in-config:
image: "service:latest"
environment:
- "CONFIG1=http://Mistral:8000"
33 changes: 33 additions & 0 deletions src/tests/fixupenv/compose.yaml.convert
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
[
{
"name": "bad-service",
"image": "somedb:latest",
"platform": 2,
"internal": true,
"ports": [
{
"target": 5432,
"mode": 1
}
],
"networks": 1
},
{
"name": "env-in-config",
"image": "service:latest",
"platform": 2,
"internal": true,
"environment": {
"CONFIG1": "http://mistral:8000"
},
"networks": 1
},
{
"name": "mistral",
"image": "mistral:latest",
Expand All @@ -25,5 +48,15 @@
}
],
"networks": 1
},
{
"name": "use-bad-service",
"image": "service:latest",
"platform": 2,
"internal": true,
"environment": {
"DB_URL": "bad-service:5432"
},
"networks": 1
}
]
19 changes: 19 additions & 0 deletions src/tests/fixupenv/compose.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,32 @@ services:
ports:
- mode: host
target: 8000
bad-service:
image: somedb:latest
networks:
default: null
ports:
- mode: ingress
target: 5432
env-in-config:
environment:
CONFIG1: http://Mistral:8000
image: service:latest
networks:
default: null
ui:
environment:
API_URL: http://Mistral:8000
SENSITIVE_DATA: null
image: ui:latest
networks:
default: null
use-bad-service:
environment:
DB_URL: bad-service:5432
image: service:latest
networks:
default: null
networks:
default:
name: fixupenv_default
11 changes: 11 additions & 0 deletions src/tests/fixupenv/compose.yaml.warnings
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
! service "Mistral": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "Mistral": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "bad-service": ingress port without healthcheck defaults to GET / HTTP/1.1
! service "bad-service": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "bad-service": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "env-in-config": environment variable "CONFIG1" overridden by config
! service "env-in-config": environment variable "CONFIG1" with value "http://Mistral:8000" contains a service name needs to be normalized, but it will be repalced by an existing config. service names in a config will not be normalized.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning about overridden env var contains a service name replacement

! service "env-in-config": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "env-in-config": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "ui": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "ui": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "ui": service names were replaced in environment variable "API_URL": "http://mock-mistral:8000"
! service "use-bad-service": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "use-bad-service": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "use-bad-service": service names were NOT replaced in environment variable "DB_URL": "bad-service:5432", only service with port mode host will be replaced
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning about service name NOT being replaced.

! service name "Mistral" was normalized to "mistral"
3 changes: 2 additions & 1 deletion src/tests/redis/compose.yaml.warnings
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
! service "y": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "y": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "z": missing compose directive: restart; assuming 'unless-stopped' (add 'restart' to silence)
! service "z": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "z": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "z": stateful service will lose data on restart; use a managed service instead
4 changes: 3 additions & 1 deletion src/tests/secretname/compose.yaml.warnings
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
! service "app": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "app": missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors
! service "app": secrets will be exposed as environment variables, not files (use 'environment' instead)
* Compressing build context for app at .
5 changes: 4 additions & 1 deletion src/tests/testproj/compose.yaml.warnings
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
! unsupported compose extension: "x-unsupported"
! No port 'mode' was specified; defaulting to 'ingress' (add 'mode: ingress' to silence)
! service "dfnx": secrets will be exposed as environment variables, not files (use 'environment' instead)
! unsupported compose extension: "x-unsupported"
* Compressing build context for dfnx at .