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
1 change: 1 addition & 0 deletions cli/azd/grpc/proto/models.proto
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ message DockerProjectOptions {
string tag = 7;
bool remote_build = 8;
repeated string build_args = 9;
string network = 10;
}

// ServiceContext defines the shared pipeline state across all phases of the service lifecycle
Expand Down
27 changes: 8 additions & 19 deletions cli/azd/pkg/azdext/mcp_security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestMCPSecurityCheckURL_BlocksMetadataEndpoints(t *testing.T) {
Expand Down Expand Up @@ -318,25 +320,12 @@ func TestMCPSecurityFluentBuilder(t *testing.T) {
RedactHeaders("Authorization").
ValidatePathsWithinBase("/tmp")

if policy == nil {
t.Fatal("fluent builder should return non-nil policy")
}

if !policy.blockMetadata {
t.Error("blockMetadata should be true")
}
if !policy.blockPrivate {
t.Error("blockPrivate should be true")
}
if !policy.requireHTTPS {
t.Error("requireHTTPS should be true")
}
if !policy.IsHeaderBlocked("Authorization") {
t.Error("Authorization should be blocked")
}
if len(policy.allowedBasePaths) != 1 {
t.Errorf("expected 1 base path, got %d", len(policy.allowedBasePaths))
}
require.NotNil(t, policy, "fluent builder should return non-nil policy")
require.True(t, policy.blockMetadata, "blockMetadata should be true")
require.True(t, policy.blockPrivate, "blockPrivate should be true")
require.True(t, policy.requireHTTPS, "requireHTTPS should be true")
require.True(t, policy.IsHeaderBlocked("Authorization"), "Authorization should be blocked")
require.Len(t, policy.allowedBasePaths, 1, "expected 1 base path")
}

func TestSSRFSafeRedirect_SchemeDowngrade(t *testing.T) {
Expand Down
18 changes: 14 additions & 4 deletions cli/azd/pkg/azdext/models.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/azd/pkg/project/container_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ func (ch *ContainerHelper) Build(
resolvedBuildArgs,
dockerOptions.BuildSecrets,
dockerEnv,
dockerOptions.Network,
previewerWriter,
)
ch.console.StopPreviewer(ctx, false)
Expand Down
1 change: 1 addition & 0 deletions cli/azd/pkg/project/framework_service_docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type DockerProjectOptions struct {
Image osutil.ExpandableString `yaml:"image,omitempty" json:"image"`
Tag osutil.ExpandableString `yaml:"tag,omitempty" json:"tag"`
RemoteBuild bool `yaml:"remoteBuild,omitempty" json:"remoteBuild,omitempty"`
Network string `yaml:"network,omitempty" json:"network,omitempty"`
BuildArgs []osutil.ExpandableString `yaml:"buildArgs,omitempty" json:"buildArgs,omitempty"`
// not supported from azure.yaml directly yet. Adding it for Aspire to use it, initially.
// Aspire would pass the secret keys, which are env vars that azd will set just to run docker build.
Expand Down
3 changes: 3 additions & 0 deletions cli/azd/pkg/project/mapper_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ func registerProjectMappings() {
Tag: tag,
RemoteBuild: src.RemoteBuild,
BuildArgs: buildArgs,
Network: src.Network,
}, nil
})

Expand Down Expand Up @@ -413,6 +414,7 @@ func registerProjectMappings() {
Image: osutil.NewExpandableString(src.Image),
Tag: osutil.NewExpandableString(src.Tag),
RemoteBuild: src.RemoteBuild,
Network: src.Network,
}

if len(src.BuildArgs) > 0 {
Expand Down Expand Up @@ -440,6 +442,7 @@ func registerProjectMappings() {
Image: osutil.NewExpandableString(src.Image),
Tag: osutil.NewExpandableString(src.Tag),
RemoteBuild: src.RemoteBuild,
Network: src.Network,
}

if len(src.BuildArgs) > 0 {
Expand Down
4 changes: 4 additions & 0 deletions cli/azd/pkg/project/mapper_registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ func TestDockerProjectOptionsMapping(t *testing.T) {
Context: ".",
Platform: "linux/amd64",
Target: "production",
Network: "host",
RemoteBuild: true,
}

Expand All @@ -412,6 +413,7 @@ func TestDockerProjectOptionsMapping(t *testing.T) {
require.Equal(t, ".", protoOptions.Context)
require.Equal(t, "linux/amd64", protoOptions.Platform)
require.Equal(t, "production", protoOptions.Target)
require.Equal(t, "host", protoOptions.Network)
require.True(t, protoOptions.RemoteBuild)
}

Expand Down Expand Up @@ -669,6 +671,7 @@ func TestFromProtoDockerProjectOptionsMapping(t *testing.T) {
Context: "..",
Platform: "linux/arm64",
Target: "test",
Network: "host",
Registry: "testregistry.azurecr.io",
Image: "testimage",
Tag: "v2.0.0",
Expand All @@ -684,6 +687,7 @@ func TestFromProtoDockerProjectOptionsMapping(t *testing.T) {
require.Equal(t, "..", dockerOptions.Context)
require.Equal(t, "linux/arm64", dockerOptions.Platform)
require.Equal(t, "test", dockerOptions.Target)
require.Equal(t, "host", dockerOptions.Network)
require.Equal(t, "testregistry.azurecr.io", dockerOptions.Registry.MustEnvsubst(func(string) string { return "" }))
require.Equal(t, "testimage", dockerOptions.Image.MustEnvsubst(func(string) string { return "" }))
require.Equal(t, "v2.0.0", dockerOptions.Tag.MustEnvsubst(func(string) string { return "" }))
Expand Down
1 change: 1 addition & 0 deletions cli/azd/pkg/tools/docker/acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func Test_DockerAcceptance(t *testing.T) {
buildArgs,
buildSecrets,
buildEnv,
"",
&buildOutput,
)
require.NoError(t, err, "build should succeed")
Expand Down
5 changes: 5 additions & 0 deletions cli/azd/pkg/tools/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func (d *Cli) Build(
buildArgs []string,
buildSecrets []string,
buildEnv []string,
buildNetwork string,
buildProgress io.Writer,
) (string, error) {
if strings.TrimSpace(platform) == "" {
Expand Down Expand Up @@ -105,6 +106,10 @@ func (d *Cli) Build(
args = append(args, "--target", target)
}

if buildNetwork != "" {
args = append(args, "--network", buildNetwork)
}

if tagName != "" {
args = append(args, "-t", tagName)
}
Expand Down
68 changes: 65 additions & 3 deletions cli/azd/pkg/tools/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func Test_DockerBuild(t *testing.T) {
buildArgs,
nil,
nil,
"",
nil,
)

Expand Down Expand Up @@ -131,6 +132,7 @@ func Test_DockerBuild(t *testing.T) {
buildArgs,
nil,
nil,
"",
nil,
)

Expand Down Expand Up @@ -188,7 +190,7 @@ func Test_DockerBuildEmptyPlatform(t *testing.T) {
})

result, err := docker.Build(
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, nil)
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, "", nil)

require.Equal(t, true, ran)
require.Nil(t, err)
Expand Down Expand Up @@ -237,7 +239,7 @@ func Test_DockerBuildArgsEmpty(t *testing.T) {
})

result, err := docker.Build(
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, nil)
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, "", nil)

require.Equal(t, true, ran)
require.Nil(t, err)
Expand Down Expand Up @@ -288,13 +290,73 @@ func Test_DockerBuildArgsMultiple(t *testing.T) {
})

result, err := docker.Build(
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, nil)
context.Background(), cwd, dockerFile, "", "", dockerContext, imageName, buildArgs, nil, nil, "", nil)

require.Equal(t, true, ran)
require.Nil(t, err)
require.Equal(t, mockedDockerImgId, result)
}

func Test_DockerBuildNetwork(t *testing.T) {
tests := []struct {
name string
network string
expectIn bool
}{
{"WithHostNetwork", "host", true},
{"WithEmptyNetwork", "", false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ran := false
cwd := "."
dockerFile := "./Dockerfile"
dockerContext := "../"
imageName := "IMAGE_NAME"

mockContext := mocks.NewMockContext(context.Background())
docker := NewCli(mockContext.CommandRunner)

mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool {
return strings.Contains(command, "docker build")
}).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) {
ran = true

argsNoFile := args.Args[:len(args.Args)-2]
value := args.Args[len(args.Args)-1]

if tt.expectIn {
require.Contains(t, argsNoFile, "--network")
require.Contains(t, argsNoFile, tt.network)
} else {
require.NotContains(t, argsNoFile, "--network")
}

err := os.WriteFile(value, []byte(mockedDockerImgId), 0600)
require.NoError(t, err)

return exec.RunResult{
Stdout: mockedDockerImgId,
ExitCode: 0,
}, nil
})

result, err := docker.Build(
context.Background(),
cwd, dockerFile, "", "",
dockerContext, imageName,
nil, nil, nil,
tt.network, nil,
)

require.True(t, ran)
require.NoError(t, err)
require.Equal(t, mockedDockerImgId, result)
})
}
}

func Test_DockerTag(t *testing.T) {
cwd := "."
imageName := "image-name"
Expand Down
11 changes: 7 additions & 4 deletions cli/azd/pkg/ux/ux_additional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ func TestConsoleWidth_empty_COLUMNS_uses_default(t *testing.T) {
func TestPtr(t *testing.T) {
intVal := 42
p := Ptr(intVal)
if p == nil {
switch {
case p == nil:
t.Fatal("Ptr should return non-nil pointer")
}
if *p != 42 {
case *p != 42:
t.Fatalf("*Ptr(42) = %d, want 42", *p)
}

strVal := "hello"
sp := Ptr(strVal)
if *sp != "hello" {
switch {
case sp == nil:
t.Fatal("Ptr should return non-nil pointer for string")
case *sp != "hello":
t.Fatalf("*Ptr(hello) = %q, want hello", *sp)
}
}
Expand Down
5 changes: 5 additions & 0 deletions schemas/alpha/azure.yaml.json
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,11 @@
"type": "string"
}
},
"network": {
"type": "string",
"title": "Optional. The networking mode for RUN instructions during docker build",
"description": "Sets the networking mode for RUN instructions during build. Passed as --network to docker build. For example, use 'host' to allow the build container to access the host network."
},
"remoteBuild": {
"type": "boolean",
"title": "Optional. Whether to build the image remotely",
Expand Down
5 changes: 5 additions & 0 deletions schemas/v1.0/azure.yaml.json
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,11 @@
"type": "string"
}
},
"network": {
"type": "string",
"title": "Optional. The networking mode for RUN instructions during docker build",
"description": "Sets the networking mode for RUN instructions during build. Passed as --network to docker build. For example, use 'host' to allow the build container to access the host network."
},
"remoteBuild": {
"type": "boolean",
"title": "Optional. Whether to build the image remotely",
Expand Down
Loading