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