github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/pkg/helm-internal/experimental/registry/client_test.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package registry
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"path/filepath"
    29  	"testing"
    30  	"time"
    31  
    32  	auth "github.com/deislabs/oras/pkg/auth/docker"
    33  	"github.com/docker/distribution/configuration"
    34  	"github.com/docker/distribution/registry"
    35  	_ "github.com/docker/distribution/registry/auth/htpasswd"
    36  	_ "github.com/docker/distribution/registry/storage/driver/inmemory"
    37  	"github.com/stretchr/testify/suite"
    38  	"golang.org/x/crypto/bcrypt"
    39  
    40  	"helm.sh/helm/v3/pkg/chart"
    41  )
    42  
    43  var (
    44  	testCacheRootDir         = "helm-registry-test"
    45  	testHtpasswdFileBasename = "authtest.htpasswd"
    46  	testUsername             = "myuser"
    47  	testPassword             = "mypass"
    48  )
    49  
    50  type RegistryClientTestSuite struct {
    51  	suite.Suite
    52  	Out                io.Writer
    53  	DockerRegistryHost string
    54  	CacheRootDir       string
    55  	RegistryClient     *Client
    56  }
    57  
    58  func (suite *RegistryClientTestSuite) SetupSuite() {
    59  	suite.CacheRootDir = testCacheRootDir
    60  	os.RemoveAll(suite.CacheRootDir)
    61  	os.Mkdir(suite.CacheRootDir, 0700)
    62  
    63  	var out bytes.Buffer
    64  	suite.Out = &out
    65  	credentialsFile := filepath.Join(suite.CacheRootDir, CredentialsFileBasename)
    66  
    67  	client, err := auth.NewClient(credentialsFile)
    68  	suite.Nil(err, "no error creating auth client")
    69  
    70  	resolver, err := client.Resolver(context.Background(), http.DefaultClient, false)
    71  	suite.Nil(err, "no error creating resolver")
    72  
    73  	// create cache
    74  	cache, err := NewCache(
    75  		CacheOptDebug(true),
    76  		CacheOptWriter(suite.Out),
    77  		CacheOptRoot(filepath.Join(suite.CacheRootDir, CacheRootDir)),
    78  	)
    79  	suite.Nil(err, "no error creating cache")
    80  
    81  	// init test client
    82  	suite.RegistryClient, err = NewClient(
    83  		ClientOptDebug(true),
    84  		ClientOptWriter(suite.Out),
    85  		ClientOptAuthorizer(&Authorizer{
    86  			Client: client,
    87  		}),
    88  		ClientOptResolver(&Resolver{
    89  			Resolver: resolver,
    90  		}),
    91  		ClientOptCache(cache),
    92  	)
    93  	suite.Nil(err, "no error creating registry client")
    94  
    95  	// create htpasswd file (w BCrypt, which is required)
    96  	pwBytes, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
    97  	suite.Nil(err, "no error generating bcrypt password for test htpasswd file")
    98  	htpasswdPath := filepath.Join(suite.CacheRootDir, testHtpasswdFileBasename)
    99  	err = ioutil.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testUsername, string(pwBytes))), 0644)
   100  	suite.Nil(err, "no error creating test htpasswd file")
   101  
   102  	// Registry config
   103  	config := &configuration.Configuration{}
   104  	port, err := getFreePort()
   105  	suite.Nil(err, "no error finding free port for test registry")
   106  	suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port)
   107  	config.HTTP.Addr = fmt.Sprintf(":%d", port)
   108  	config.HTTP.DrainTimeout = time.Duration(10) * time.Second
   109  	config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
   110  	config.Auth = configuration.Auth{
   111  		"htpasswd": configuration.Parameters{
   112  			"realm": "localhost",
   113  			"path":  htpasswdPath,
   114  		},
   115  	}
   116  	dockerRegistry, err := registry.NewRegistry(context.Background(), config)
   117  	suite.Nil(err, "no error creating test registry")
   118  
   119  	// Start Docker registry
   120  	go dockerRegistry.ListenAndServe()
   121  }
   122  
   123  func (suite *RegistryClientTestSuite) TearDownSuite() {
   124  	os.RemoveAll(suite.CacheRootDir)
   125  }
   126  
   127  func (suite *RegistryClientTestSuite) Test_0_Login() {
   128  	err := suite.RegistryClient.Login(suite.DockerRegistryHost, "badverybad", "ohsobad", false)
   129  	suite.NotNil(err, "error logging into registry with bad credentials")
   130  
   131  	err = suite.RegistryClient.Login(suite.DockerRegistryHost, "badverybad", "ohsobad", true)
   132  	suite.NotNil(err, "error logging into registry with bad credentials, insecure mode")
   133  
   134  	err = suite.RegistryClient.Login(suite.DockerRegistryHost, testUsername, testPassword, false)
   135  	suite.Nil(err, "no error logging into registry with good credentials")
   136  
   137  	err = suite.RegistryClient.Login(suite.DockerRegistryHost, testUsername, testPassword, true)
   138  	suite.Nil(err, "no error logging into registry with good credentials, insecure mode")
   139  }
   140  
   141  func (suite *RegistryClientTestSuite) Test_1_SaveChart() {
   142  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   143  	suite.Nil(err)
   144  
   145  	// empty chart
   146  	err = suite.RegistryClient.SaveChart(&chart.Chart{}, ref)
   147  	suite.NotNil(err)
   148  
   149  	// valid chart
   150  	ch := &chart.Chart{}
   151  	ch.Metadata = &chart.Metadata{
   152  		APIVersion: "v1",
   153  		Name:       "testchart",
   154  		Version:    "1.2.3",
   155  	}
   156  	err = suite.RegistryClient.SaveChart(ch, ref)
   157  	suite.Nil(err)
   158  }
   159  
   160  func (suite *RegistryClientTestSuite) Test_2_LoadChart() {
   161  
   162  	// non-existent ref
   163  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   164  	suite.Nil(err)
   165  	_, err = suite.RegistryClient.LoadChart(ref)
   166  	suite.NotNil(err)
   167  
   168  	// existing ref
   169  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   170  	suite.Nil(err)
   171  	ch, err := suite.RegistryClient.LoadChart(ref)
   172  	suite.Nil(err)
   173  	suite.Equal("testchart", ch.Metadata.Name)
   174  	suite.Equal("1.2.3", ch.Metadata.Version)
   175  }
   176  
   177  func (suite *RegistryClientTestSuite) Test_3_PushChart() {
   178  
   179  	// non-existent ref
   180  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   181  	suite.Nil(err)
   182  	err = suite.RegistryClient.PushChart(ref)
   183  	suite.NotNil(err)
   184  
   185  	// existing ref
   186  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   187  	suite.Nil(err)
   188  	err = suite.RegistryClient.PushChart(ref)
   189  	suite.Nil(err)
   190  }
   191  
   192  func (suite *RegistryClientTestSuite) Test_4_PullChart() {
   193  
   194  	// non-existent ref
   195  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   196  	suite.Nil(err)
   197  	err = suite.RegistryClient.PullChart(ref)
   198  	suite.NotNil(err)
   199  
   200  	// existing ref
   201  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   202  	suite.Nil(err)
   203  	err = suite.RegistryClient.PullChart(ref)
   204  	suite.Nil(err)
   205  }
   206  
   207  func (suite *RegistryClientTestSuite) Test_5_PrintChartTable() {
   208  	err := suite.RegistryClient.PrintChartTable()
   209  	suite.Nil(err)
   210  }
   211  
   212  func (suite *RegistryClientTestSuite) Test_6_RemoveChart() {
   213  
   214  	// non-existent ref
   215  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   216  	suite.Nil(err)
   217  	err = suite.RegistryClient.RemoveChart(ref)
   218  	suite.NotNil(err)
   219  
   220  	// existing ref
   221  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   222  	suite.Nil(err)
   223  	err = suite.RegistryClient.RemoveChart(ref)
   224  	suite.Nil(err)
   225  }
   226  
   227  func (suite *RegistryClientTestSuite) Test_7_Logout() {
   228  	err := suite.RegistryClient.Logout("this-host-aint-real:5000")
   229  	suite.NotNil(err, "error logging out of registry that has no entry")
   230  
   231  	err = suite.RegistryClient.Logout(suite.DockerRegistryHost)
   232  	suite.Nil(err, "no error logging out of registry")
   233  }
   234  
   235  func TestRegistryClientTestSuite(t *testing.T) {
   236  	suite.Run(t, new(RegistryClientTestSuite))
   237  }
   238  
   239  // borrowed from https://github.com/phayes/freeport
   240  func getFreePort() (int, error) {
   241  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
   242  	if err != nil {
   243  		return 0, err
   244  	}
   245  
   246  	l, err := net.ListenTCP("tcp", addr)
   247  	if err != nil {
   248  		return 0, err
   249  	}
   250  	defer l.Close()
   251  	return l.Addr().(*net.TCPAddr).Port, nil
   252  }