Skip to content
Open
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
5 changes: 4 additions & 1 deletion pkg/helm/actions/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func GetActionConfigurations(host, ns, token string, transport *http.RoundTrippe
}
conf := new(action.Configuration)
conf.Init(confFlags, ns, "secrets", klog.Infof)

err = GetDefaultOCIRegistry(conf)
if err != nil {
klog.V(4).Infof("Failed to get default OCI registry: %v", err)
}
Comment on lines 52 to +56
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, @sowmya-sl, but I didn't fully appreciate what you had accomplished here!

So, I now have to ask: why implement GetDefaultOCIRegistry() as a separate function, or, more to the point, why call it from here? Why not in-line or call it inside conf.Init()?

Also, as an aside (since it's arguably outside the scope of this PR), my IDE notes that the code is ignoring the error returned by conf.Init(). 😞 We should probably not be doing that -- instead, the code should be returning nil (and maybe an error) and letting the caller address the situation. But, I don't know if you're up for trying to fix that, now (it affects a dozen callers, as you presumably know well).

return conf
}
42 changes: 42 additions & 0 deletions pkg/helm/actions/get_registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package actions

import (
"crypto/tls"
"fmt"
"net/http"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/registry"
)

// newRegistryClient is a package-level variable to allow mocking in tests
var newRegistryClient = registry.NewClient

func GetDefaultOCIRegistry(conf *action.Configuration) error {
return GetOCIRegistry(conf, false, false)
}

func GetOCIRegistry(conf *action.Configuration, skipTLSVerify bool, plainHTTP bool) error {
if conf == nil {
return fmt.Errorf("action configuration cannot be nil")
}
opts := []registry.ClientOption{
registry.ClientOptDebug(false),
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}
if skipTLSVerify {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
opts = append(opts, registry.ClientOptHTTPClient(&http.Client{Transport: transport}))
}
registryClient, err := newRegistryClient(opts...)
if err != nil {
return fmt.Errorf("failed to create registry client: %w", err)
}
conf.RegistryClient = registryClient
return nil
}
132 changes: 132 additions & 0 deletions pkg/helm/actions/get_registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package actions

import (
"errors"
"io"
"testing"

"github.com/stretchr/testify/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
)

func TestGetDefaultOCIRegistry_Success(t *testing.T) {
store := storage.Init(driver.NewMemory())
conf := &action.Configuration{
RESTClientGetter: FakeConfig{},
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
Capabilities: chartutil.DefaultCapabilities,
}
require.Nil(t, conf.RegistryClient, "Registry Client should be nil")

// Store original values
originalReleases := conf.Releases
originalKubeClient := conf.KubeClient
originalCapabilities := conf.Capabilities

err := GetDefaultOCIRegistry(conf)
require.NoError(t, err)
require.NotNil(t, conf.RegistryClient, "Registry Client should not be nil")

// Verify other configuration fields are not modified.
require.Equal(t, originalReleases, conf.Releases, "Releases should not be modified")
require.Equal(t, originalKubeClient, conf.KubeClient, "KubeClient should not be modified")
require.Equal(t, originalCapabilities, conf.Capabilities, "Capabilities should not be modified")

}

func TestGetOCIRegistry_NilConfig(t *testing.T) {
err := GetOCIRegistry(nil, false, false)
require.Error(t, err)
require.Contains(t, err.Error(), "action configuration cannot be nil")
}

func TestGetOCIRegistry_Success(t *testing.T) {
tests := []struct {
name string
skipTLSVerify bool
plainHTTP bool
}{
{
name: "default options",
skipTLSVerify: false,
plainHTTP: false,
},
{
name: "with skipTLSVerify",
skipTLSVerify: true,
plainHTTP: false,
},
{
name: "with plainHTTP",
skipTLSVerify: false,
plainHTTP: true,
},
{
name: "with both skipTLSVerify and plainHTTP",
skipTLSVerify: true,
plainHTTP: true,
},
}
originalNewRegistryClient := newRegistryClient
defer func() {
newRegistryClient = originalNewRegistryClient
}()

for _, tt := range tests {
newRegistryClient = func(options ...registry.ClientOption) (*registry.Client, error) {
count := 0
if tt.plainHTTP {
count += 1
}
if tt.skipTLSVerify {
count += 1
}
require.Equal(t, count, len(options)-1, "Expected %d options, got %d", count, len(options))
return &registry.Client{}, nil
}
t.Run(tt.name, func(t *testing.T) {
store := storage.Init(driver.NewMemory())
conf := &action.Configuration{
RESTClientGetter: FakeConfig{},
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
Capabilities: chartutil.DefaultCapabilities,
}
require.Nil(t, conf.RegistryClient, "Registry Client should be nil initially")

err := GetOCIRegistry(conf, tt.skipTLSVerify, tt.plainHTTP)
require.NoError(t, err)
require.NotNil(t, conf.RegistryClient, "Registry Client should not be nil after GetOCIRegistry")
})
}
}

func TestGetOCIRegistry_NewClientError(t *testing.T) {
Comment on lines +48 to +110

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this in the test.

// Save original function and restore after test
originalNewRegistryClient := newRegistryClient
defer func() { newRegistryClient = originalNewRegistryClient }()

// Mock newRegistryClient to return an error
newRegistryClient = func(options ...registry.ClientOption) (*registry.Client, error) {
return nil, errors.New("mock registry client error")
}

store := storage.Init(driver.NewMemory())
conf := &action.Configuration{
RESTClientGetter: FakeConfig{},
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
Capabilities: chartutil.DefaultCapabilities,
}

err := GetOCIRegistry(conf, false, false)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to create registry client")
require.Contains(t, err.Error(), "mock registry client error")
}