diff --git a/config/example-runner-1.yaml b/config/example-runner-1.yaml index 1f7109ea..9c9b437f 100644 --- a/config/example-runner-1.yaml +++ b/config/example-runner-1.yaml @@ -9,7 +9,7 @@ control_plane: manager_version: v0.2.1-nddev.45 scheduling_mode: scale-set provider: incus - provider_version: v0.1.5-nddev.46 + provider_version: v0.1.5-nddev.47 provider_interface: v0.1.0 worker_kind: incus-container runner: actions/runner diff --git a/config/example-runner-2.yaml b/config/example-runner-2.yaml index 344b6136..832afef8 100644 --- a/config/example-runner-2.yaml +++ b/config/example-runner-2.yaml @@ -9,7 +9,7 @@ control_plane: manager_version: v0.2.1-nddev.45 scheduling_mode: scale-set provider: incus - provider_version: v0.1.5-nddev.46 + provider_version: v0.1.5-nddev.47 provider_interface: v0.1.0 worker_kind: incus-container runner: actions/runner diff --git a/config/example-runner-3.yaml b/config/example-runner-3.yaml index 7f61e554..7c0597f1 100644 --- a/config/example-runner-3.yaml +++ b/config/example-runner-3.yaml @@ -9,7 +9,7 @@ control_plane: manager_version: v0.2.1-nddev.45 scheduling_mode: scale-set provider: incus - provider_version: v0.1.5-nddev.46 + provider_version: v0.1.5-nddev.47 provider_interface: v0.1.0 worker_kind: incus-container runner: actions/runner diff --git a/config/example-runner-4.yaml b/config/example-runner-4.yaml index b04e42ae..473c5ba0 100644 --- a/config/example-runner-4.yaml +++ b/config/example-runner-4.yaml @@ -9,7 +9,7 @@ control_plane: manager_version: v0.2.1-nddev.45 scheduling_mode: scale-set provider: incus - provider_version: v0.1.5-nddev.46 + provider_version: v0.1.5-nddev.47 provider_interface: v0.1.0 worker_kind: incus-container runner: actions/runner diff --git a/config/example-services.yaml b/config/example-services.yaml index 58fe5816..c5b7df0e 100644 --- a/config/example-services.yaml +++ b/config/example-services.yaml @@ -27,7 +27,7 @@ control_plane: manager_version: v0.2.1-nddev.45 scheduling_mode: scale-set provider: incus - provider_version: v0.1.5-nddev.46 + provider_version: v0.1.5-nddev.47 provider_interface: v0.1.0 worker_kind: incus-container runner: actions/runner diff --git a/config/provider-derivative.yaml b/config/provider-derivative.yaml index b4db58bf..fe0abc5f 100644 --- a/config/provider-derivative.yaml +++ b/config/provider-derivative.yaml @@ -16,7 +16,7 @@ artifact: garm-provider-incus # state all move together, because all three derive from here. A provider change # that does not bump it ships under the previous version, which is exactly how # runner-1 and runner-2 diverged. -derivative_version: v0.1.5-nddev.46 +derivative_version: v0.1.5-nddev.47 # The external-provider protocol GARM speaks to this binary. It moves on its own # schedule -- a provider release does not imply an interface release -- so it is @@ -36,8 +36,8 @@ runtime: incus_sdk_version: v7.3.0 build: - source_commit: 036858e4c28b5886653d1f70c1359e51a019c084 - binary_sha256: 24dcce1ef9083de0f2f910875c936a5e7917858fcda284d856f716f9b783d791 + source_commit: ecb85b68a7f0543ede48a43f72b00b94c77296e3 + binary_sha256: a77323695b17a715d04276700b1c96f71457c28c406950cc2ce20b20de591094 go_version: go1.26.6 cgo_enabled: false target_os: linux diff --git a/internal/garmproviderincus/provider/incus.go b/internal/garmproviderincus/provider/incus.go index 4c5b72d5..11ab50bb 100644 --- a/internal/garmproviderincus/provider/incus.go +++ b/internal/garmproviderincus/provider/incus.go @@ -1187,12 +1187,18 @@ func canonicalRepositoryIdentity(value string) (string, error) { } func (l *Incus) narrowBootstrapRepository(ctx context.Context, bootstrap commonParams.BootstrapInstance) (commonParams.BootstrapInstance, error) { - if l.isRegisteredRepositoryURL(bootstrap.RepoURL) { - return bootstrap, nil - } - if _, err := canonicalRepositoryIdentity(bootstrap.RepoURL); err == nil { + identity, identityErr := canonicalRepositoryIdentity(bootstrap.RepoURL) + if identityErr == nil && strings.Contains(identity, "/") { + if !l.isRegisteredRepositoryURL(bootstrap.RepoURL) { + return bootstrap, runnerErrors.NewBadRequestError( + "repository is outside the configured provider boundary: %q", bootstrap.RepoURL) + } return bootstrap, nil } + // An organization entity gives GARM only the account URL. It is an allowed + // account but not an executable repository identity; resolve the one exact + // repository from the active pre-AcquireJobs intent before any admission, + // cache or instance metadata is derived from it. resolver, ok := l.admission.(repositoryResolver) if !ok { return bootstrap, fmt.Errorf("repository resolver is not configured") @@ -1203,6 +1209,10 @@ func (l *Incus) narrowBootstrapRepository(ctx context.Context, bootstrap commonP "repository %q cannot be narrowed through queue intent: %s", bootstrap.RepoURL, err) } bootstrap.RepoURL = "https://github.com/" + repository + if !l.isRegisteredRepositoryURL(bootstrap.RepoURL) { + return bootstrap, runnerErrors.NewBadRequestError( + "queue intent resolved repository outside the configured provider boundary: %q", repository) + } return bootstrap, nil } diff --git a/internal/garmproviderincus/provider/incus_test.go b/internal/garmproviderincus/provider/incus_test.go index 3c1d7453..2679da34 100644 --- a/internal/garmproviderincus/provider/incus_test.go +++ b/internal/garmproviderincus/provider/incus_test.go @@ -1003,16 +1003,17 @@ func TestOrganizationBootstrapIsNarrowedThroughQueueIntent(t *testing.T) { require.ErrorContains(t, err, "cannot be narrowed through queue intent") } -func TestWholeAccountBootstrapRetainsAccountIdentity(t *testing.T) { +func TestWholeAccountBootstrapWithoutTrailingSlashIsNarrowed(t *testing.T) { bootstrap := validBootstrap() bootstrap.RepoURL = "https://github.com/example-org" - provider := &Incus{} + provider := newTestProvider(new(MockIncusServer)) + provider.admission = repositoryResolvingAdmission{repository: "example-org/example-actions"} resolved, err := provider.narrowBootstrapRepository(context.Background(), bootstrap) require.NoError(t, err) - require.Equal(t, bootstrap.RepoURL, resolved.RepoURL) + require.Equal(t, "https://github.com/example-org/example-actions", resolved.RepoURL) identity, err := canonicalRepositoryIdentity(resolved.RepoURL) require.NoError(t, err) - require.Equal(t, "example-org", identity) + require.Equal(t, "example-org/example-actions", identity) } func TestGetCreateInstanceArgsRejectsImageAliasDrift(t *testing.T) {