mirror of https://github.com/helm/helm
helm alias set: configure an alias that maps a name to an oci url used like with legacy repositories `@name` or `alias:name` helm alias list: shows aliases and substitutions helm alias substitute: configure registry url substitutions for example substitute oci://some-vendor.example.com/vendor/charts with oci://internal.example.com/charts/3rdparty/vendor and thus helm will never contact some-vendor.example.com but instead resolve the vendors charts using internal.example.com Signed-off-by: Christoph Obexer <cobexer@gmail.com>pull/11771/head
parent
ff61915cda
commit
3976c0621a
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"helm.sh/helm/v4/pkg/action"
|
||||||
|
)
|
||||||
|
|
||||||
|
const aliasHelp = `
|
||||||
|
This command consists of multiple subcommands to interact with OCI aliases.
|
||||||
|
`
|
||||||
|
|
||||||
|
func newAliasCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "alias",
|
||||||
|
Short: "manage OCI aliases and substitutions",
|
||||||
|
Long: aliasHelp,
|
||||||
|
}
|
||||||
|
cmd.AddCommand(
|
||||||
|
newAliasListCmd(cfg, out),
|
||||||
|
newAliasSetCmd(cfg, out),
|
||||||
|
newAliasSubstituteCmd(cfg, out),
|
||||||
|
)
|
||||||
|
return cmd
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/gosuri/uitable"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"helm.sh/helm/v4/pkg/action"
|
||||||
|
"helm.sh/helm/v4/pkg/cli/output"
|
||||||
|
"helm.sh/helm/v4/pkg/cmd/require"
|
||||||
|
"helm.sh/helm/v4/pkg/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const aliasListDesc = `
|
||||||
|
List registry aliases and substitutions.
|
||||||
|
`
|
||||||
|
|
||||||
|
func newAliasListCmd(_ *action.Configuration, out io.Writer) *cobra.Command {
|
||||||
|
var aliasesOpt, substitutionsOpt bool
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "list aliases and substitutions",
|
||||||
|
Long: aliasListDesc,
|
||||||
|
Args: require.NoArgs,
|
||||||
|
ValidArgsFunction: noMoreArgsCompFunc,
|
||||||
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
|
var err error
|
||||||
|
a, _ := registry.LoadAliasesFile(settings.RegistryAliasConfig)
|
||||||
|
|
||||||
|
if aliasesOpt || !substitutionsOpt {
|
||||||
|
table := uitable.New()
|
||||||
|
table.AddRow("ALIAS", "URL")
|
||||||
|
for a, url := range a.Aliases {
|
||||||
|
table.AddRow(a, url)
|
||||||
|
}
|
||||||
|
err = output.EncodeTable(out, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
if substitutionsOpt || !aliasesOpt {
|
||||||
|
table := uitable.New()
|
||||||
|
table.AddRow("SUBSTITUTION", "REPLACEMENT")
|
||||||
|
for s, r := range a.Substitutions {
|
||||||
|
table.AddRow(s, r)
|
||||||
|
}
|
||||||
|
err = output.EncodeTable(out, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
f := cmd.Flags()
|
||||||
|
f.BoolVarP(&aliasesOpt, "aliases", "a", false, "list aliases")
|
||||||
|
f.BoolVarP(&substitutionsOpt, "substitutions", "s", false, "list substitutions")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"helm.sh/helm/v4/pkg/action"
|
||||||
|
"helm.sh/helm/v4/pkg/cmd/require"
|
||||||
|
"helm.sh/helm/v4/pkg/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const aliasSetDesc = `
|
||||||
|
Set or remove an alias for an OCI registry.
|
||||||
|
`
|
||||||
|
|
||||||
|
func newAliasSetCmd(_ *action.Configuration, _ io.Writer) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "set NAME [URL]",
|
||||||
|
Short: "configure the named alias",
|
||||||
|
Long: aliasSetDesc,
|
||||||
|
Args: require.MinimumNArgs(1),
|
||||||
|
ValidArgsFunction: noMoreArgsCompFunc,
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
alias := args[0]
|
||||||
|
var value *string
|
||||||
|
if len(args) > 1 {
|
||||||
|
value = &args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
err := setAlias(settings.RegistryAliasConfig, alias, value)
|
||||||
|
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAlias(aliasesFile, alias string, value *string) error {
|
||||||
|
if strings.Contains(alias, "/") {
|
||||||
|
return fmt.Errorf("alias name (%s) contains '/', please specify a different name without '/'", alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := registry.LoadAliasesFile(aliasesFile)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return fmt.Errorf("failed to load aliases: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
a.SetAlias(alias, *value)
|
||||||
|
} else {
|
||||||
|
a.RemoveAlias(alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.WriteAliasesFile(aliasesFile, 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"helm.sh/helm/v4/pkg/action"
|
||||||
|
"helm.sh/helm/v4/pkg/cmd/require"
|
||||||
|
"helm.sh/helm/v4/pkg/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const aliasSubstituteDesc = `
|
||||||
|
Set or remove a registry substitution.
|
||||||
|
`
|
||||||
|
|
||||||
|
func newAliasSubstituteCmd(_ *action.Configuration, _ io.Writer) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "substitute URL [URL]",
|
||||||
|
Short: "configure a OCI registry URL substitution",
|
||||||
|
Long: aliasSubstituteDesc,
|
||||||
|
Args: require.MinimumNArgs(1),
|
||||||
|
ValidArgsFunction: noMoreArgsCompFunc,
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
substitution := args[0]
|
||||||
|
var replacement *string
|
||||||
|
if len(args) > 1 {
|
||||||
|
replacement = &args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
err := setSubstitution(settings.RegistryAliasConfig, substitution, replacement)
|
||||||
|
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSubstitution(aliasesFile, substitution string, replacement *string) error {
|
||||||
|
a, err := registry.LoadAliasesFile(aliasesFile)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return fmt.Errorf("failed to load aliases: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if replacement != nil {
|
||||||
|
a.SetSubstitution(substitution, *replacement)
|
||||||
|
} else {
|
||||||
|
a.RemoveSubstitution(substitution)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.WriteAliasesFile(aliasesFile, 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Aliases represents the registry/aliases.yaml file
|
||||||
|
type Aliases struct {
|
||||||
|
APIVersion string `json:"apiVersion"`
|
||||||
|
Aliases map[string]string `json:"aliases"`
|
||||||
|
Substitutions map[string]string `json:"substitutions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAliasesFile generates an empty aliases file.
|
||||||
|
//
|
||||||
|
// APIVersion is automatically set.
|
||||||
|
func NewAliasesFile() *Aliases {
|
||||||
|
return &Aliases{
|
||||||
|
APIVersion: APIVersionV1,
|
||||||
|
Aliases: map[string]string{},
|
||||||
|
Substitutions: map[string]string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAliasesFile takes a file at the given path and returns an Aliases object
|
||||||
|
func LoadAliasesFile(path string) (*Aliases, error) {
|
||||||
|
a := NewAliasesFile()
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return a, fmt.Errorf("couldn't load aliases file (%s): %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, a)
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAlias adds or updates an alias.
|
||||||
|
func (a *Aliases) SetAlias(alias, url string) {
|
||||||
|
a.Aliases[alias] = url
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAlias removes the entry from the list of repository aliases.
|
||||||
|
// RemoveAlias returns true if the alias existed before it was deleted.
|
||||||
|
func (a *Aliases) RemoveAlias(alias string) bool {
|
||||||
|
_, existing := a.Aliases[alias]
|
||||||
|
delete(a.Aliases, alias)
|
||||||
|
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSubstitution adds or updates a substitution.
|
||||||
|
func (a *Aliases) SetSubstitution(substitution, replacement string) {
|
||||||
|
a.Substitutions[substitution] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSubstitution removes the substitution and returns true if the
|
||||||
|
// substitution existed before it was deleted.
|
||||||
|
func (a *Aliases) RemoveSubstitution(substitution string) bool {
|
||||||
|
_, existing := a.Substitutions[substitution]
|
||||||
|
delete(a.Substitutions, substitution)
|
||||||
|
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand first expands aliases to their mapped value end then performs
|
||||||
|
// prefix substitutions until no substitution matches or each substitution
|
||||||
|
// was used at most once.
|
||||||
|
func (a *Aliases) Expand(source string) string {
|
||||||
|
return a.performSubstitutions(a.expandAlias(source))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) expandAlias(source string) string {
|
||||||
|
isAtAlias := strings.HasPrefix(source, "@")
|
||||||
|
isLongAlias := strings.HasPrefix(source, "alias:")
|
||||||
|
if isAtAlias || isLongAlias {
|
||||||
|
var alias string
|
||||||
|
if isAtAlias {
|
||||||
|
alias = strings.TrimPrefix(source, "@")
|
||||||
|
} else if isLongAlias {
|
||||||
|
alias = strings.TrimPrefix(source, "alias:")
|
||||||
|
}
|
||||||
|
if v, existing := a.Aliases[alias]; existing {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) performSubstitutions(source string) string {
|
||||||
|
current := source
|
||||||
|
|
||||||
|
// no recursions
|
||||||
|
used := make(map[string]bool, len(a.Substitutions))
|
||||||
|
orderedSubstitutions := make([]string, 0, len(a.Substitutions))
|
||||||
|
for k := range a.Substitutions {
|
||||||
|
orderedSubstitutions = append(orderedSubstitutions, k)
|
||||||
|
}
|
||||||
|
sort.SliceStable(orderedSubstitutions, func(i, j int) bool {
|
||||||
|
return len(orderedSubstitutions[i]) < len(orderedSubstitutions[j])
|
||||||
|
})
|
||||||
|
var changed bool
|
||||||
|
for {
|
||||||
|
changed = false
|
||||||
|
for i := range orderedSubstitutions {
|
||||||
|
k := orderedSubstitutions[i]
|
||||||
|
if !used[k] && strings.HasPrefix(current, k) {
|
||||||
|
used[k] = true
|
||||||
|
current = a.Substitutions[k] + strings.TrimPrefix(current, k)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAliasesFile writes an aliases file to the given path.
|
||||||
|
func (a *Aliases) WriteAliasesFile(path string, perm os.FileMode) error {
|
||||||
|
data, err := yaml.Marshal(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(path, data, perm)
|
||||||
|
}
|
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testAliasesFile = "testdata/aliases.yaml"
|
||||||
|
|
||||||
|
func TestAliasesFile(t *testing.T) {
|
||||||
|
a := NewAliasesFile()
|
||||||
|
a.SetAlias("staging", "oci://example.com/charts/staging")
|
||||||
|
a.SetAlias("production", "oci://example.com/charts/production")
|
||||||
|
|
||||||
|
a.SetSubstitution("oci://example.com/charts/production", "oci://example.com/qa-environment/charts/production")
|
||||||
|
|
||||||
|
if len(a.Aliases) != 2 {
|
||||||
|
t.Fatal("Expected 2 aliases")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Substitutions) != 1 {
|
||||||
|
t.Fatal("Expected 1 substitution")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.RemoveAlias("staging") {
|
||||||
|
t.Fatal("Expected staging alias to exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.RemoveAlias("staging") {
|
||||||
|
t.Fatal("Expected staging alias to not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Aliases) != 1 {
|
||||||
|
t.Fatal("Expected 1 alias")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.RemoveSubstitution("oci://example.com/charts/production") {
|
||||||
|
t.Fatal("Expected 'oci://example.com/charts/production' substitution to exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.RemoveSubstitution("oci://example.com/charts/production") {
|
||||||
|
t.Fatal("Expected 'oci://example.com/charts/production' substitution to not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Substitutions) != 0 {
|
||||||
|
t.Fatal("Expected 0 substitutions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAliasesFile(t *testing.T) {
|
||||||
|
expects := NewAliasesFile()
|
||||||
|
expects.SetAlias("staging", "oci://example.com/charts/staging")
|
||||||
|
expects.SetAlias("production", "oci://example.com/charts/production")
|
||||||
|
expects.SetAlias("dev", "oci://example.com/charts/dev")
|
||||||
|
expects.SetSubstitution("oci://example.com/charts/dev", "oci://dev.example.com/charts")
|
||||||
|
expects.SetSubstitution("oci://example.com/charts/staging", "oci://staging.example.com/charts")
|
||||||
|
expects.SetSubstitution("https://example.com/stable/charts", "oci://stable.example.com/charts")
|
||||||
|
|
||||||
|
file, err := LoadAliasesFile(testAliasesFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%q could not be loaded: %s", testAliasesFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expects.APIVersion, file.APIVersion) {
|
||||||
|
t.Fatalf("Unexpected apiVersion: %#v", file.APIVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expects.Aliases, file.Aliases) {
|
||||||
|
t.Fatalf("Unexpected aliases: %#v", file.Aliases)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expects.Substitutions, file.Substitutions) {
|
||||||
|
t.Fatalf("Unexpected substitutions: %#v", file.Substitutions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAliasesFile(t *testing.T) {
|
||||||
|
expects := NewAliasesFile()
|
||||||
|
expects.SetAlias("dev", "oci://example.com/charts/dev")
|
||||||
|
expects.SetSubstitution("oci://example.com/charts/dev", "oci://dev.example.com/charts")
|
||||||
|
|
||||||
|
file, err := os.CreateTemp(t.TempDir(), "helm-aliases")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create test-file (%v)", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
if err := expects.WriteAliasesFile(file.Name(), 0o644); err != nil {
|
||||||
|
t.Errorf("failed to write file (%v)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases, err := LoadAliasesFile(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to load file (%v)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expects, aliases) {
|
||||||
|
t.Errorf("aliases inconsistent after saving and reloading:\nexpected: %#v\nactual: %#v", expects, aliases)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasNotExists(t *testing.T) {
|
||||||
|
if _, err := LoadAliasesFile("/this/path/does/not/exist.yaml"); err == nil {
|
||||||
|
t.Errorf("expected err to be non-nil when path does not exist")
|
||||||
|
} else if !strings.Contains(err.Error(), "couldn't load aliases file") {
|
||||||
|
t.Errorf("expected prompt `couldn't load aliases file`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliases_performSubstitutions(t *testing.T) {
|
||||||
|
substitutions := NewAliasesFile()
|
||||||
|
substitutions.SetSubstitution("oci://example.com/charts", "oci://example.com/charts/dev")
|
||||||
|
substitutions.SetSubstitution("oci://length.example.com", "oci://shorter.length.example.com")
|
||||||
|
substitutions.SetSubstitution("oci://length.example.com/charts", "oci://longer.length.example.com/charts")
|
||||||
|
substitutions.SetSubstitution("oci://multiple.example.com", "oci://example.com/charts")
|
||||||
|
substitutions.SetSubstitution("oci://localhost:5000/", "oci://staging.example.com/charts/")
|
||||||
|
substitutions.SetSubstitution("https://example.com/vendor", "oci://vendor.example.com/charts/")
|
||||||
|
substitutions.SetSubstitution("oci://one.example.com", "oci://two.example.com")
|
||||||
|
substitutions.SetSubstitution("oci://two.example.com", "oci://one.example.com")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
source string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basicOCIReplacement",
|
||||||
|
source: "oci://localhost:5000/myrepo",
|
||||||
|
want: "oci://staging.example.com/charts/myrepo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exacltyAsRequested",
|
||||||
|
source: "https://example.com/vendor-dev/some-chart-repo",
|
||||||
|
want: "oci://vendor.example.com/charts/-dev/some-chart-repo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multipleReplacements",
|
||||||
|
source: "oci://multiple.example.com/myrepo",
|
||||||
|
want: "oci://example.com/charts/dev/myrepo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "norecursion",
|
||||||
|
source: "oci://one.example.com/myrepo",
|
||||||
|
want: "oci://one.example.com/myrepo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "usedOnlyOnce",
|
||||||
|
source: "oci://example.com/charts/myrepo",
|
||||||
|
want: "oci://example.com/charts/dev/myrepo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := substitutions.performSubstitutions(tt.source); got != tt.want {
|
||||||
|
t.Errorf("Aliases.performSubstitutions() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
aliases:
|
||||||
|
staging: oci://example.com/charts/staging
|
||||||
|
production: oci://example.com/charts/production
|
||||||
|
dev: oci://example.com/charts/dev
|
||||||
|
substitutions:
|
||||||
|
oci://example.com/charts/dev: oci://dev.example.com/charts
|
||||||
|
oci://example.com/charts/staging: oci://staging.example.com/charts
|
||||||
|
https://example.com/stable/charts: oci://stable.example.com/charts
|
Loading…
Reference in new issue