github.com/azure-devops-engineer/helm@v3.0.0-alpha.2+incompatible/pkg/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  	// Init test client
    73  	suite.RegistryClient = NewClient(&ClientOptions{
    74  		Out: suite.Out,
    75  		Authorizer: Authorizer{
    76  			Client: client,
    77  		},
    78  		Resolver: Resolver{
    79  			Resolver: resolver,
    80  		},
    81  		CacheRootDir: suite.CacheRootDir,
    82  	})
    83  
    84  	// create htpasswd file (w BCrypt, which is required)
    85  	pwBytes, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
    86  	suite.Nil(err, "no error generating bcrypt password for test htpasswd file")
    87  	htpasswdPath := filepath.Join(suite.CacheRootDir, testHtpasswdFileBasename)
    88  	err = ioutil.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testUsername, string(pwBytes))), 0644)
    89  	suite.Nil(err, "no error creating test htpasswd file")
    90  
    91  	// Registry config
    92  	config := &configuration.Configuration{}
    93  	port, err := getFreePort()
    94  	suite.Nil(err, "no error finding free port for test registry")
    95  	suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port)
    96  	config.HTTP.Addr = fmt.Sprintf(":%d", port)
    97  	config.HTTP.DrainTimeout = time.Duration(10) * time.Second
    98  	config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
    99  	config.Auth = configuration.Auth{
   100  		"htpasswd": configuration.Parameters{
   101  			"realm": "localhost",
   102  			"path":  htpasswdPath,
   103  		},
   104  	}
   105  	dockerRegistry, err := registry.NewRegistry(context.Background(), config)
   106  	suite.Nil(err, "no error creating test registry")
   107  
   108  	// Start Docker registry
   109  	go dockerRegistry.ListenAndServe()
   110  }
   111  
   112  func (suite *RegistryClientTestSuite) TearDownSuite() {
   113  	os.RemoveAll(suite.CacheRootDir)
   114  }
   115  
   116  func (suite *RegistryClientTestSuite) Test_0_Login() {
   117  	err := suite.RegistryClient.Login(suite.DockerRegistryHost, "badverybad", "ohsobad")
   118  	suite.NotNil(err, "error logging into registry with bad credentials")
   119  
   120  	err = suite.RegistryClient.Login(suite.DockerRegistryHost, testUsername, testPassword)
   121  	suite.Nil(err, "no error logging into registry with good credentials")
   122  }
   123  
   124  func (suite *RegistryClientTestSuite) Test_1_SaveChart() {
   125  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   126  	suite.Nil(err)
   127  
   128  	// empty chart
   129  	err = suite.RegistryClient.SaveChart(&chart.Chart{}, ref)
   130  	suite.NotNil(err)
   131  
   132  	// valid chart
   133  	ch := &chart.Chart{}
   134  	ch.Metadata = &chart.Metadata{
   135  		APIVersion: "v1",
   136  		Name:       "testchart",
   137  		Version:    "1.2.3",
   138  	}
   139  	err = suite.RegistryClient.SaveChart(ch, ref)
   140  	suite.Nil(err)
   141  }
   142  
   143  func (suite *RegistryClientTestSuite) Test_2_LoadChart() {
   144  
   145  	// non-existent ref
   146  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   147  	suite.Nil(err)
   148  	ch, err := suite.RegistryClient.LoadChart(ref)
   149  	suite.NotNil(err)
   150  
   151  	// existing ref
   152  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   153  	suite.Nil(err)
   154  	ch, err = suite.RegistryClient.LoadChart(ref)
   155  	suite.Nil(err)
   156  	suite.Equal("testchart", ch.Metadata.Name)
   157  	suite.Equal("1.2.3", ch.Metadata.Version)
   158  }
   159  
   160  func (suite *RegistryClientTestSuite) Test_3_PushChart() {
   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.PushChart(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  	err = suite.RegistryClient.PushChart(ref)
   172  	suite.Nil(err)
   173  }
   174  
   175  func (suite *RegistryClientTestSuite) Test_4_PullChart() {
   176  
   177  	// non-existent ref
   178  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   179  	suite.Nil(err)
   180  	err = suite.RegistryClient.PullChart(ref)
   181  	suite.NotNil(err)
   182  
   183  	// existing ref
   184  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   185  	suite.Nil(err)
   186  	err = suite.RegistryClient.PullChart(ref)
   187  	suite.Nil(err)
   188  }
   189  
   190  func (suite *RegistryClientTestSuite) Test_5_PrintChartTable() {
   191  	err := suite.RegistryClient.PrintChartTable()
   192  	suite.Nil(err)
   193  }
   194  
   195  func (suite *RegistryClientTestSuite) Test_6_RemoveChart() {
   196  
   197  	// non-existent ref
   198  	ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost))
   199  	suite.Nil(err)
   200  	err = suite.RegistryClient.RemoveChart(ref)
   201  	suite.NotNil(err)
   202  
   203  	// existing ref
   204  	ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost))
   205  	suite.Nil(err)
   206  	err = suite.RegistryClient.RemoveChart(ref)
   207  	suite.Nil(err)
   208  }
   209  
   210  func (suite *RegistryClientTestSuite) Test_7_Logout() {
   211  	err := suite.RegistryClient.Logout("this-host-aint-real:5000")
   212  	suite.NotNil(err, "error logging out of registry that has no entry")
   213  
   214  	err = suite.RegistryClient.Logout(suite.DockerRegistryHost)
   215  	suite.Nil(err, "no error logging out of registry")
   216  }
   217  
   218  func TestRegistryClientTestSuite(t *testing.T) {
   219  	suite.Run(t, new(RegistryClientTestSuite))
   220  }
   221  
   222  // borrowed from https://github.com/phayes/freeport
   223  func getFreePort() (int, error) {
   224  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
   225  	if err != nil {
   226  		return 0, err
   227  	}
   228  
   229  	l, err := net.ListenTCP("tcp", addr)
   230  	if err != nil {
   231  		return 0, err
   232  	}
   233  	defer l.Close()
   234  	return l.Addr().(*net.TCPAddr).Port, nil
   235  }