Skip to content
Closed
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
64 changes: 43 additions & 21 deletions internal/infra/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,17 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
return fmt.Errorf("failed to create Docker client: %w", err)
}

// Determine if we have credentials - if not, we can skip the proxy and networks
// Exception: networks are still needed for case-insensitive filesystem support
hasCredentials := len(params.Creds) > 0
needsNetwork := hasCredentials || params.Job.UseCaseInsensitiveFileSystem()

if params.PullImages {
err = pullImage(ctx, cli, params.ProxyImage)
if err != nil {
return err
if hasCredentials {
err = pullImage(ctx, cli, params.ProxyImage)
if err != nil {
return err
}
}

if params.CollectorConfigPath != "" {
Expand All @@ -396,29 +403,37 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
}
}

networks, err := NewNetworks(ctx, cli)
if err != nil {
return fmt.Errorf("failed to create networks: %w", err)
// Create networks if we have credentials to protect or need case-insensitive filesystem
var networks *Networks
var prox *Proxy
if needsNetwork {
networks, err = NewNetworks(ctx, cli)
if err != nil {
return fmt.Errorf("failed to create networks: %w", err)
}
defer networks.Close()
}
defer networks.Close()

prox, err := NewProxy(ctx, cli, &params, networks)
if err != nil {
return err
}
defer func() {
if proxyErr := prox.Close(); proxyErr != nil {
err = proxyErr
// Only create proxy if we have credentials to protect
if hasCredentials {
prox, err = NewProxy(ctx, cli, &params, networks)
if err != nil {
return err
}
}()
defer func() {
if proxyErr := prox.Close(); proxyErr != nil {
err = proxyErr
}
}()

// proxy logs interfere with debugging output
if !params.Debug {
go prox.TailLogs(ctx, cli)
// proxy logs interfere with debugging output
if !params.Debug {
go prox.TailLogs(ctx, cli)
}
}

var collector *Collector
if params.CollectorConfigPath != "" {
if params.CollectorConfigPath != "" && hasCredentials {
collector, err = NewCollector(ctx, cli, networks, &params, prox)
if err != nil {
fmt.Println("Failed to create OpenTelemetry collector:", err)
Expand All @@ -427,6 +442,8 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
go collector.TailLogs(ctx, cli)
}
defer collector.Close()
} else if params.CollectorConfigPath != "" && !hasCredentials {
log.Println("Warning: OpenTelemetry collector requires credentials to be present, skipping collector setup")
}

updater, err := NewUpdater(ctx, cli, networks, &params, prox, collector)
Expand Down Expand Up @@ -456,13 +473,18 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
return err
}

proxyURL := ""
if prox != nil {
proxyURL = prox.url
}

if params.Debug {
if err := updater.RunShell(ctx, prox.url, params.ApiUrl, params.Job, params.UpdaterEnvironmentVariables); err != nil {
if err := updater.RunShell(ctx, proxyURL, params.ApiUrl, params.Job, params.UpdaterEnvironmentVariables); err != nil {
return err
}
} else {
// Run dependabot commands as a dependabot user
env := userEnv(prox.url, params.ApiUrl, params.Job, params.UpdaterEnvironmentVariables)
env := userEnv(proxyURL, params.ApiUrl, params.Job, params.UpdaterEnvironmentVariables)
if params.Flamegraph {
env = append(env, "FLAMEGRAPH=1")
}
Expand Down
35 changes: 35 additions & 0 deletions internal/infra/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,38 @@ func Test_generateIgnoreConditions(t *testing.T) {
}
})
}

func Test_hasCredentials(t *testing.T) {
t.Run("returns false when credentials are nil", func(t *testing.T) {
params := RunParams{
Creds: nil,
}
hasCredentials := len(params.Creds) > 0
if hasCredentials {
t.Error("expected hasCredentials to be false with nil credentials")
}
})

t.Run("returns false when credentials are empty", func(t *testing.T) {
params := RunParams{
Creds: []model.Credential{},
}
hasCredentials := len(params.Creds) > 0
if hasCredentials {
t.Error("expected hasCredentials to be false with empty credentials")
}
})

t.Run("returns true when credentials exist", func(t *testing.T) {
params := RunParams{
Creds: []model.Credential{{
"type": "test",
"token": "test_token",
}},
}
hasCredentials := len(params.Creds) > 0
if !hasCredentials {
t.Error("expected hasCredentials to be true with credentials")
}
})
}
32 changes: 22 additions & 10 deletions internal/infra/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,16 @@ func NewUpdater(ctx context.Context, cli *client.Client, net *Networks, params *
}
}

netCfg := &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
net.noInternetName: {
NetworkID: net.NoInternet.ID,
var netCfg *network.NetworkingConfig
// Only configure networking if networks exist (when credentials are present)
if net != nil {
netCfg = &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
net.noInternetName: {
NetworkID: net.NoInternet.ID,
},
},
},
}
}

updaterContainer, err := cli.ContainerCreate(ctx, containerCfg, hostCfg, netCfg, nil, "")
Expand All @@ -128,7 +132,12 @@ func NewUpdater(ctx context.Context, cli *client.Client, net *Networks, params *
storageVolumes: storageVolumes,
}

if err = putUpdaterInputs(ctx, cli, prox.ca.Cert, updaterContainer.ID, params.Job); err != nil {
// Get the cert from proxy if it exists, otherwise use empty string
cert := ""
if prox != nil {
cert = prox.ca.Cert
}
if err = putUpdaterInputs(ctx, cli, cert, updaterContainer.ID, params.Job); err != nil {
updater.Close()
return nil, err
}
Expand Down Expand Up @@ -265,10 +274,13 @@ func addStorageMounts(hostCfg *container.HostConfig, storageContainerAddress str

func putUpdaterInputs(ctx context.Context, cli *client.Client, cert, id string, job *model.Job) error {
opt := container.CopyToContainerOptions{}
if t, err := tarball(dbotCert, cert); err != nil {
return fmt.Errorf("failed to create cert tarball: %w", err)
} else if err = cli.CopyToContainer(ctx, id, "/", t, opt); err != nil {
return fmt.Errorf("failed to copy cert to container: %w", err)
// Only copy cert if it exists (when proxy is present)
if cert != "" {
if t, err := tarball(dbotCert, cert); err != nil {
return fmt.Errorf("failed to create cert tarball: %w", err)
} else if err = cli.CopyToContainer(ctx, id, "/", t, opt); err != nil {
return fmt.Errorf("failed to copy cert to container: %w", err)
}
}

data, err := JobFile{Job: job}.ToJSON()
Expand Down
37 changes: 34 additions & 3 deletions testdata/scripts/proxy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,51 @@
exec docker build -qt proxy-updater .
exec docker build -qt dummy-proxy -f Dockerfile.proxy .

dependabot update go_modules dependabot/cli --updater-image proxy-updater --proxy-image dummy-proxy
# Test with credentials (proxy should run)
dependabot update -f job.yml --updater-image proxy-updater --proxy-image dummy-proxy
stderr 'proxy \| Proxy is running'
stderr 'updater \| Updater is running'
! stderr 'proxy \| custom-ca-cert\.crt'

dependabot update go_modules dependabot/cli --proxy-cert my-cert --updater-image proxy-updater --proxy-image dummy-proxy
dependabot update -f job.yml --proxy-cert my-cert --updater-image proxy-updater --proxy-image dummy-proxy
stderr 'proxy \| custom-ca-cert\.crt'
stderr 'proxy \| I am a certificate'

# Test that the CLI exits with non-zero if the proxy does too.
! dependabot update go_modules dependabot/cli --proxy-cert crash --updater-image proxy-updater --proxy-image dummy-proxy --proxy-username user --proxy-password pass
! dependabot update -f job.yml --proxy-cert crash --updater-image proxy-updater --proxy-image dummy-proxy

# Test without credentials (proxy should NOT run)
dependabot update -f job-no-creds.yml --updater-image proxy-updater --proxy-image dummy-proxy
! stderr 'proxy \| Proxy is running'
stderr 'updater \| Updater is running'

exec docker rmi -f proxy-updater dummy-proxy

-- job.yml --
job:
package-manager: go_modules
allowed-updates:
- update-type: all
source:
provider: github
repo: dependabot/cli
directory: /
credentials:
- type: git_source
host: github.com
username: x-access-token
password: dummy_token

-- job-no-creds.yml --
job:
package-manager: go_modules
allowed-updates:
- update-type: all
source:
provider: github
repo: dependabot/cli
directory: /

-- crash --
crash

Expand Down
17 changes: 16 additions & 1 deletion testdata/scripts/pypi.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# this test verifies the certificate generated allows us to connect to pypi.org
exec docker build -qt pypi-updater .

dependabot update go_modules dependabot/cli --updater-image pypi-updater
dependabot update -f job.yml --updater-image pypi-updater
stderr '200 https://pypi.org:443/'

exec docker rmi -f pypi-updater

-- job.yml --
job:
package-manager: go_modules
allowed-updates:
- update-type: all
source:
provider: github
repo: dependabot/cli
directory: /
credentials:
- type: git_source
host: github.com
username: x-access-token
password: dummy_token

-- Dockerfile --
FROM python:3.13-bookworm

Expand Down
Loading