-
Notifications
You must be signed in to change notification settings - Fork 148
/
common.go
137 lines (116 loc) · 4 KB
/
common.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors
// Package oci contains functions for interacting with Zarf packages stored in OCI registries.
package oci
import (
"context"
"fmt"
"net/http"
"strings"
zarfconfig "github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/registry"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
)
const (
// ZarfLayerMediaTypeBlob is the media type for all Zarf layers due to the range of possible content
ZarfLayerMediaTypeBlob = "application/vnd.zarf.layer.v1.blob"
// SkeletonSuffix is the reference suffix used for skeleton packages
SkeletonSuffix = "skeleton"
)
// OrasRemote is a wrapper around the Oras remote repository that includes a progress bar for interactive feedback.
type OrasRemote struct {
repo *remote.Repository
ctx context.Context
Transport *utils.Transport
CopyOpts oras.CopyOptions
client *auth.Client
}
// NewOrasRemote returns an oras remote repository client and context for the given url.
//
// Registry auth is handled by the Docker CLI's credential store and checked before returning the client
func NewOrasRemote(url string) (*OrasRemote, error) {
ref, err := registry.ParseReference(strings.TrimPrefix(url, utils.OCIURLPrefix))
if err != nil {
return nil, fmt.Errorf("failed to parse OCI reference: %w", err)
}
o := &OrasRemote{}
o.ctx = context.TODO()
err = o.WithRepository(ref)
if err != nil {
return nil, err
}
copyOpts := oras.DefaultCopyOptions
copyOpts.OnCopySkipped = o.printLayerSuccess
copyOpts.PostCopy = o.printLayerSuccess
o.CopyOpts = copyOpts
return o, nil
}
// WithRepository sets the repository for the remote as well as the auth client.
func (o *OrasRemote) WithRepository(ref registry.Reference) error {
// patch docker.io to registry-1.docker.io
// this allows end users to use docker.io as an alias for registry-1.docker.io
if ref.Registry == "docker.io" {
ref.Registry = "registry-1.docker.io"
}
client, err := o.withAuthClient(ref)
if err != nil {
return err
}
o.client = client
repo, err := remote.NewRepository(ref.String())
if err != nil {
return err
}
repo.PlainHTTP = zarfconfig.CommonOptions.Insecure
repo.Client = o.client
o.repo = repo
return nil
}
// withAuthClient returns an auth client for the given reference.
//
// The credentials are pulled using Docker's default credential store.
func (o *OrasRemote) withAuthClient(ref registry.Reference) (*auth.Client, error) {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig.InsecureSkipVerify = zarfconfig.CommonOptions.Insecure
o.Transport = utils.NewTransport(transport, nil)
client := &auth.Client{
Cache: auth.DefaultCache,
Client: &http.Client{
Transport: o.Transport,
},
}
client.SetUserAgent("zarf/" + zarfconfig.CLIVersion)
message.Debugf("Loading docker config file from default config location: %s", config.Dir())
cfg, err := config.Load(config.Dir())
if err != nil {
return nil, err
}
if !cfg.ContainsAuth() {
message.Debug("no docker config file found, run 'zarf tools registry login --help'")
return client, nil
}
configs := []*configfile.ConfigFile{cfg}
var key = ref.Registry
if key == "registry-1.docker.io" {
// Docker stores its credentials under the following key, otherwise credentials use the registry URL
key = "https://index.docker.io/v1/"
}
authConf, err := configs[0].GetCredentialsStore(key).Get(key)
if err != nil {
return nil, fmt.Errorf("unable to get credentials for %s: %w", key, err)
}
cred := auth.Credential{
Username: authConf.Username,
Password: authConf.Password,
AccessToken: authConf.RegistryToken,
RefreshToken: authConf.IdentityToken,
}
client.Credential = auth.StaticCredential(ref.Registry, cred)
return client, nil
}