github.com/azure-devops-engineer/helm@v3.0.0-alpha.2+incompatible/pkg/registry/client.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 // import "helm.sh/helm/pkg/registry"
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  
    24  	orascontent "github.com/deislabs/oras/pkg/content"
    25  	orascontext "github.com/deislabs/oras/pkg/context"
    26  	"github.com/deislabs/oras/pkg/oras"
    27  	"github.com/gosuri/uitable"
    28  	"github.com/sirupsen/logrus"
    29  
    30  	"helm.sh/helm/pkg/chart"
    31  )
    32  
    33  const (
    34  	CredentialsFileBasename = "config.json"
    35  )
    36  
    37  type (
    38  	// ClientOptions is used to construct a new client
    39  	ClientOptions struct {
    40  		Debug        bool
    41  		Out          io.Writer
    42  		Authorizer   Authorizer
    43  		Resolver     Resolver
    44  		CacheRootDir string
    45  	}
    46  
    47  	// Client works with OCI-compliant registries and local Helm chart cache
    48  	Client struct {
    49  		debug      bool
    50  		out        io.Writer
    51  		authorizer Authorizer
    52  		resolver   Resolver
    53  		cache      *filesystemCache // TODO: something more robust
    54  	}
    55  )
    56  
    57  // NewClient returns a new registry client with config
    58  func NewClient(options *ClientOptions) *Client {
    59  	return &Client{
    60  		debug:      options.Debug,
    61  		out:        options.Out,
    62  		resolver:   options.Resolver,
    63  		authorizer: options.Authorizer,
    64  		cache: &filesystemCache{
    65  			out:     options.Out,
    66  			rootDir: options.CacheRootDir,
    67  			store:   orascontent.NewMemoryStore(),
    68  		},
    69  	}
    70  }
    71  
    72  // Login logs into a registry
    73  func (c *Client) Login(hostname string, username string, password string) error {
    74  	err := c.authorizer.Login(c.newContext(), hostname, username, password)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	fmt.Fprint(c.out, "Login succeeded\n")
    79  	return nil
    80  }
    81  
    82  // Logout logs out of a registry
    83  func (c *Client) Logout(hostname string) error {
    84  	err := c.authorizer.Logout(c.newContext(), hostname)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	fmt.Fprint(c.out, "Logout succeeded\n")
    89  	return nil
    90  }
    91  
    92  // PushChart uploads a chart to a registry
    93  func (c *Client) PushChart(ref *Reference) error {
    94  	c.setDefaultTag(ref)
    95  	fmt.Fprintf(c.out, "The push refers to repository [%s]\n", ref.Repo)
    96  	layers, err := c.cache.LoadReference(ref)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	_, err = oras.Push(c.newContext(), c.resolver, ref.String(), c.cache.store, layers,
   101  		oras.WithConfigMediaType(HelmChartConfigMediaType))
   102  	if err != nil {
   103  		return err
   104  	}
   105  	var totalSize int64
   106  	for _, layer := range layers {
   107  		totalSize += layer.Size
   108  	}
   109  	fmt.Fprintf(c.out,
   110  		"%s: pushed to remote (%d layers, %s total)\n", ref.Tag, len(layers), byteCountBinary(totalSize))
   111  	return nil
   112  }
   113  
   114  // PullChart downloads a chart from a registry
   115  func (c *Client) PullChart(ref *Reference) error {
   116  	c.setDefaultTag(ref)
   117  	fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Tag, ref.Repo)
   118  	_, layers, err := oras.Pull(c.newContext(), c.resolver, ref.String(), c.cache.store, oras.WithAllowedMediaTypes(KnownMediaTypes()))
   119  	if err != nil {
   120  		return err
   121  	}
   122  	exists, err := c.cache.StoreReference(ref, layers)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	if !exists {
   127  		fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s:%s\n", ref.Repo, ref.Tag)
   128  	} else {
   129  		fmt.Fprintf(c.out, "Status: Chart is up to date for %s:%s\n", ref.Repo, ref.Tag)
   130  	}
   131  	return nil
   132  }
   133  
   134  // SaveChart stores a copy of chart in local cache
   135  func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error {
   136  	c.setDefaultTag(ref)
   137  	layers, err := c.cache.ChartToLayers(ch)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	_, err = c.cache.StoreReference(ref, layers)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	fmt.Fprintf(c.out, "%s: saved\n", ref.Tag)
   146  	return nil
   147  }
   148  
   149  // LoadChart retrieves a chart object by reference
   150  func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) {
   151  	c.setDefaultTag(ref)
   152  	layers, err := c.cache.LoadReference(ref)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	ch, err := c.cache.LayersToChart(layers)
   157  	return ch, err
   158  }
   159  
   160  // RemoveChart deletes a locally saved chart
   161  func (c *Client) RemoveChart(ref *Reference) error {
   162  	c.setDefaultTag(ref)
   163  	err := c.cache.DeleteReference(ref)
   164  	if err != nil {
   165  		return err
   166  	}
   167  	fmt.Fprintf(c.out, "%s: removed\n", ref.Tag)
   168  	return err
   169  }
   170  
   171  // PrintChartTable prints a list of locally stored charts
   172  func (c *Client) PrintChartTable() error {
   173  	table := uitable.New()
   174  	table.MaxColWidth = 60
   175  	table.AddRow("REF", "NAME", "VERSION", "DIGEST", "SIZE", "CREATED")
   176  	rows, err := c.cache.TableRows()
   177  	if err != nil {
   178  		return err
   179  	}
   180  	for _, row := range rows {
   181  		table.AddRow(row...)
   182  	}
   183  	fmt.Fprintln(c.out, table.String())
   184  	return nil
   185  }
   186  
   187  func (c *Client) setDefaultTag(ref *Reference) {
   188  	if ref.Tag == "" {
   189  		ref.Tag = HelmChartDefaultTag
   190  		fmt.Fprintf(c.out, "Using default tag: %s\n", HelmChartDefaultTag)
   191  	}
   192  }
   193  
   194  // disable verbose logging coming from ORAS unless debug is enabled
   195  func (c *Client) newContext() context.Context {
   196  	if !c.debug {
   197  		return orascontext.Background()
   198  	}
   199  	ctx := orascontext.WithLoggerFromWriter(context.Background(), c.out)
   200  	orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel)
   201  	return ctx
   202  }