Merge 6cf2e0b522 into daebbfb0ad
This commit is contained in:
commit
8c55dc7c01
|
|
@ -1819,7 +1819,16 @@ repositories:
|
|||
oci: true
|
||||
```
|
||||
|
||||
It is important not to include a scheme for the URL as helm requires that these are not present for OCI registries
|
||||
It is important not to include a scheme for the URL as helm requires that these are not present for OCI registries.
|
||||
|
||||
The URL can optionally include an organization or repository path. Helmfile will automatically extract the registry hostname for authentication:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- name: myOCIRegistry
|
||||
url: quay.io/my-organization
|
||||
oci: true
|
||||
```
|
||||
|
||||
Secondly the credentials for the OCI registry can either be specified within `helmfile.yaml` similar to
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ type DiffKey struct {
|
|||
type Helm struct {
|
||||
Charts []string
|
||||
Repo []string
|
||||
RegistryLoginHost string // Captures the registry host for OCI login
|
||||
Releases []Release
|
||||
Deleted []Release
|
||||
Linted []Release
|
||||
|
|
@ -104,6 +105,7 @@ func (helm *Helm) UpdateRepo() error {
|
|||
return nil
|
||||
}
|
||||
func (helm *Helm) RegistryLogin(name, username, password, caFile, certFile, keyFile string, skipTLSVerify bool) error {
|
||||
helm.RegistryLoginHost = name
|
||||
return nil
|
||||
}
|
||||
func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart, namespace string, flags ...string) error {
|
||||
|
|
|
|||
|
|
@ -592,7 +592,10 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
|
|||
username, password := gatherUsernamePassword(repo.Name, repo.Username, repo.Password)
|
||||
var err error
|
||||
if repo.OCI {
|
||||
err = helm.RegistryLogin(repo.URL, username, password, repo.CaFile, repo.CertFile, repo.KeyFile, repo.SkipTLSVerify)
|
||||
// For OCI registries, extract just the registry host for login
|
||||
// helm registry login expects "registry.io" not "registry.io/org/path"
|
||||
registryHost := extractRegistryHost(repo.URL)
|
||||
err = helm.RegistryLogin(registryHost, username, password, repo.CaFile, repo.CertFile, repo.KeyFile, repo.SkipTLSVerify)
|
||||
} else {
|
||||
err = helm.AddRepo(repo.Name, repo.URL, repo.CaFile, repo.CertFile, repo.KeyFile, username, password, repo.Managed, repo.PassCredentials, repo.SkipTLSVerify)
|
||||
}
|
||||
|
|
@ -607,6 +610,23 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
|
|||
return updated, nil
|
||||
}
|
||||
|
||||
// extractRegistryHost extracts the registry hostname (and optional port) from a URL.
|
||||
// For OCI registries, helm registry login requires just the host, not the full path.
|
||||
// Examples:
|
||||
// - "quay.io/org/repo" -> "quay.io"
|
||||
// - "registry:443/helm-charts" -> "registry:443"
|
||||
// - "myregistry.azurecr.io" -> "myregistry.azurecr.io"
|
||||
func extractRegistryHost(repoURL string) string {
|
||||
// Find the first slash after the initial part
|
||||
slashIndex := strings.Index(repoURL, "/")
|
||||
if slashIndex == -1 {
|
||||
// No slash, return the whole URL
|
||||
return repoURL
|
||||
}
|
||||
// Return everything before the first slash
|
||||
return repoURL[:slashIndex]
|
||||
}
|
||||
|
||||
func gatherUsernamePassword(repoName string, username string, password string) (string, string) {
|
||||
var user, pass string
|
||||
|
||||
|
|
|
|||
|
|
@ -1248,6 +1248,103 @@ func TestHelmState_SyncRepos(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHelmState_SyncRepos_OCI(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repos []RepositorySpec
|
||||
envs map[string]string
|
||||
wantRegistryHost string
|
||||
}{
|
||||
{
|
||||
name: "OCI registry with organization path",
|
||||
repos: []RepositorySpec{
|
||||
{
|
||||
Name: "ociregistry",
|
||||
URL: "quay.io/ORG_NAME",
|
||||
OCI: true,
|
||||
Username: "testuser",
|
||||
Password: "testpass",
|
||||
},
|
||||
},
|
||||
wantRegistryHost: "quay.io",
|
||||
},
|
||||
{
|
||||
name: "OCI registry with multiple path segments",
|
||||
repos: []RepositorySpec{
|
||||
{
|
||||
Name: "myregistry",
|
||||
URL: "registry.example.com/org/team/repo",
|
||||
OCI: true,
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
},
|
||||
},
|
||||
wantRegistryHost: "registry.example.com",
|
||||
},
|
||||
{
|
||||
name: "OCI registry without path",
|
||||
repos: []RepositorySpec{
|
||||
{
|
||||
Name: "simpleregistry",
|
||||
URL: "myregistry.azurecr.io",
|
||||
OCI: true,
|
||||
Username: "azureuser",
|
||||
Password: "azurepass",
|
||||
},
|
||||
},
|
||||
wantRegistryHost: "myregistry.azurecr.io",
|
||||
},
|
||||
{
|
||||
name: "OCI registry with port and path",
|
||||
repos: []RepositorySpec{
|
||||
{
|
||||
Name: "localregistry",
|
||||
URL: "localhost:5000/charts",
|
||||
OCI: true,
|
||||
Username: "local",
|
||||
Password: "local",
|
||||
},
|
||||
},
|
||||
wantRegistryHost: "localhost:5000",
|
||||
},
|
||||
{
|
||||
name: "OCI registry with environment credentials",
|
||||
repos: []RepositorySpec{
|
||||
{
|
||||
Name: "envregistry",
|
||||
URL: "registry.io/org",
|
||||
OCI: true,
|
||||
},
|
||||
},
|
||||
envs: map[string]string{
|
||||
"ENVREGISTRY_USERNAME": "envuser",
|
||||
"ENVREGISTRY_PASSWORD": "envpass",
|
||||
},
|
||||
wantRegistryHost: "registry.io",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for k, v := range tt.envs {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
helm := &exectest.Helm{}
|
||||
state := &HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Repositories: tt.repos,
|
||||
},
|
||||
}
|
||||
_, err := state.SyncRepos(helm, map[string]bool{})
|
||||
if err != nil {
|
||||
t.Errorf("HelmState.SyncRepos() error = %v", err)
|
||||
}
|
||||
if helm.RegistryLoginHost != tt.wantRegistryHost {
|
||||
t.Errorf("HelmState.SyncRepos() RegistryLoginHost = %q, want %q", helm.RegistryLoginHost, tt.wantRegistryHost)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelmState_SyncReleases(t *testing.T) {
|
||||
postRenderer := "foo.sh"
|
||||
tests := []struct {
|
||||
|
|
@ -3095,6 +3192,53 @@ func TestReverse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_extractRegistryHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repoURL string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "URL with organization and repository path",
|
||||
repoURL: "quay.io/ORG_NAME",
|
||||
expected: "quay.io",
|
||||
},
|
||||
{
|
||||
name: "URL with registry port and path",
|
||||
repoURL: "registry:443/helm-charts",
|
||||
expected: "registry:443",
|
||||
},
|
||||
{
|
||||
name: "URL with multiple path segments",
|
||||
repoURL: "registry.io/org/repo/subpath",
|
||||
expected: "registry.io",
|
||||
},
|
||||
{
|
||||
name: "URL with registry only, no path",
|
||||
repoURL: "myregistry.azurecr.io",
|
||||
expected: "myregistry.azurecr.io",
|
||||
},
|
||||
{
|
||||
name: "URL with registry and port only",
|
||||
repoURL: "localhost:5000",
|
||||
expected: "localhost:5000",
|
||||
},
|
||||
{
|
||||
name: "Docker Hub style URL",
|
||||
repoURL: "registry-1.docker.io/library",
|
||||
expected: "registry-1.docker.io",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := extractRegistryHost(tt.repoURL)
|
||||
if got != tt.expected {
|
||||
t.Errorf("extractRegistryHost(%q) = %q, want %q", tt.repoURL, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_gatherUsernamePassword(t *testing.T) {
|
||||
type args struct {
|
||||
repoName string
|
||||
|
|
|
|||
Loading…
Reference in New Issue