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 }