Upgrade k8s package verison (#5358)
* upgrade k8s package version Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> * Script upgrade and code formatting. Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
123
vendor/oras.land/oras-go/pkg/auth/docker/client.go
vendored
Normal file
123
vendor/oras.land/oras-go/pkg/auth/docker/client.go
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright The ORAS Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"oras.land/oras-go/pkg/auth"
|
||||
)
|
||||
|
||||
// Client provides authentication operations for docker registries.
|
||||
type Client struct {
|
||||
configs []*configfile.ConfigFile
|
||||
}
|
||||
|
||||
// NewClient creates a new auth client based on provided config paths.
|
||||
// If not config path is provided, the default path is used.
|
||||
// Credentials are read from the first config and fall backs to next.
|
||||
// All changes will only be written to the first config file.
|
||||
func NewClient(configPaths ...string) (auth.Client, error) {
|
||||
if len(configPaths) == 0 {
|
||||
cfg, err := config.Load(config.Dir())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !cfg.ContainsAuth() {
|
||||
cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
configs: []*configfile.ConfigFile{cfg},
|
||||
}, nil
|
||||
}
|
||||
|
||||
var configs []*configfile.ConfigFile
|
||||
for _, path := range configPaths {
|
||||
cfg, err := loadConfigFile(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, path)
|
||||
}
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
configs: configs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewClientWithDockerFallback creates a new auth client
|
||||
// which falls back on Docker's default config path.
|
||||
// This allows support for ~/.docker/config.json as a fallback,
|
||||
// as well as support for the DOCKER_CONFIG environment variable.
|
||||
func NewClientWithDockerFallback(configPaths ...string) (auth.Client, error) {
|
||||
if len(configPaths) == 0 {
|
||||
return NewClient()
|
||||
}
|
||||
|
||||
var configs []*configfile.ConfigFile
|
||||
for _, path := range configPaths {
|
||||
cfg, err := loadConfigFile(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, path)
|
||||
}
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
|
||||
// Add the Docker default config last
|
||||
dockerFallbackCfg, err := config.Load(config.Dir())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dockerFallbackCfg.ContainsAuth() {
|
||||
dockerFallbackCfg.CredentialsStore = credentials.DetectDefaultStore(dockerFallbackCfg.CredentialsStore)
|
||||
}
|
||||
configs = append(configs, dockerFallbackCfg)
|
||||
|
||||
return &Client{
|
||||
configs: configs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) primaryCredentialsStore(hostname string) credentials.Store {
|
||||
return c.configs[0].GetCredentialsStore(hostname)
|
||||
}
|
||||
|
||||
// loadConfigFile reads the configuration files from the given path.
|
||||
func loadConfigFile(path string) (*configfile.ConfigFile, error) {
|
||||
cfg := configfile.New(path)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
if err := cfg.LoadFromReader(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if !cfg.ContainsAuth() {
|
||||
cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
103
vendor/oras.land/oras-go/pkg/auth/docker/login.go
vendored
Normal file
103
vendor/oras.land/oras-go/pkg/auth/docker/login.go
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright The ORAS Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ctypes "github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/registry"
|
||||
|
||||
iface "oras.land/oras-go/pkg/auth"
|
||||
)
|
||||
|
||||
const IndexHostname = "index.docker.io"
|
||||
|
||||
// Login logs in to a docker registry identified by the hostname.
|
||||
// Deprecated: use LoginWithOpts
|
||||
func (c *Client) Login(ctx context.Context, hostname, username, secret string, insecure bool) error {
|
||||
settings := &iface.LoginSettings{
|
||||
Context: ctx,
|
||||
Hostname: hostname,
|
||||
Username: username,
|
||||
Secret: secret,
|
||||
Insecure: insecure,
|
||||
}
|
||||
return c.login(settings)
|
||||
}
|
||||
|
||||
// LoginWithOpts logs in to a docker registry identified by the hostname with custom options.
|
||||
func (c *Client) LoginWithOpts(options ...iface.LoginOption) error {
|
||||
settings := &iface.LoginSettings{}
|
||||
for _, option := range options {
|
||||
option(settings)
|
||||
}
|
||||
return c.login(settings)
|
||||
}
|
||||
|
||||
func (c *Client) login(settings *iface.LoginSettings) error {
|
||||
hostname := resolveHostname(settings.Hostname)
|
||||
cred := types.AuthConfig{
|
||||
Username: settings.Username,
|
||||
ServerAddress: hostname,
|
||||
}
|
||||
if settings.Username == "" {
|
||||
cred.IdentityToken = settings.Secret
|
||||
} else {
|
||||
cred.Password = settings.Secret
|
||||
}
|
||||
|
||||
opts := registry.ServiceOptions{}
|
||||
|
||||
if settings.Insecure {
|
||||
opts.InsecureRegistries = []string{hostname}
|
||||
}
|
||||
|
||||
// Login to ensure valid credential
|
||||
remote, err := registry.NewService(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := settings.Context
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
userAgent := settings.UserAgent
|
||||
if userAgent == "" {
|
||||
userAgent = "oras"
|
||||
}
|
||||
|
||||
var token string
|
||||
if (settings.CertFile != "" && settings.KeyFile != "") || settings.CAFile != "" {
|
||||
_, token, err = c.loginWithTLS(ctx, remote, settings.CertFile, settings.KeyFile, settings.CAFile, &cred, userAgent)
|
||||
} else {
|
||||
_, token, err = remote.Auth(ctx, &cred, userAgent)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if token != "" {
|
||||
cred.Username = ""
|
||||
cred.Password = ""
|
||||
cred.IdentityToken = token
|
||||
}
|
||||
|
||||
// Store credential
|
||||
return c.primaryCredentialsStore(hostname).Store(ctypes.AuthConfig(cred))
|
||||
}
|
||||
220
vendor/oras.land/oras-go/pkg/auth/docker/login_tls.go
vendored
Normal file
220
vendor/oras.land/oras-go/pkg/auth/docker/login_tls.go
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Copyright The ORAS Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// The following functions are adapted from github.com/docker/docker/registry
|
||||
// We need these to support passing in a transport that has custom TLS configuration
|
||||
// They are not exposed in the docker/registry package that's why they are copied here
|
||||
|
||||
type loginCredentialStore struct {
|
||||
authConfig *types.AuthConfig
|
||||
}
|
||||
|
||||
func (lcs loginCredentialStore) Basic(*url.URL) (string, string) {
|
||||
return lcs.authConfig.Username, lcs.authConfig.Password
|
||||
}
|
||||
|
||||
func (lcs loginCredentialStore) RefreshToken(*url.URL, string) string {
|
||||
return lcs.authConfig.IdentityToken
|
||||
}
|
||||
|
||||
func (lcs loginCredentialStore) SetRefreshToken(u *url.URL, service, token string) {
|
||||
lcs.authConfig.IdentityToken = token
|
||||
}
|
||||
|
||||
// loginWithTLS tries to login to the v2 registry server.
|
||||
// A custom tls.Config is used to override the default TLS configuration of the different registry endpoints.
|
||||
// The tls.Config is created using the provided certificate, certificate key and certificate authority.
|
||||
func (c *Client) loginWithTLS(ctx context.Context, service registry.Service, certFile, keyFile, caFile string, authConfig *types.AuthConfig, userAgent string) (string, string, error) {
|
||||
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{CAFile: caFile, CertFile: certFile, KeyFile: keyFile})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
endpoints, err := c.getEndpoints(authConfig.ServerAddress, service)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var status, token string
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint.TLSConfig = tlsConfig
|
||||
status, token, err = loginV2(authConfig, endpoint, userAgent)
|
||||
|
||||
if err != nil {
|
||||
if isNotAuthorizedError(err) {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
logrus.WithError(err).Infof("Error logging in to endpoint, trying next endpoint")
|
||||
continue
|
||||
}
|
||||
|
||||
return status, token, nil
|
||||
}
|
||||
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// getEndpoints returns the endpoints for the given hostname.
|
||||
func (c *Client) getEndpoints(address string, service registry.Service) ([]registry.APIEndpoint, error) {
|
||||
var registryHostName = IndexHostname
|
||||
|
||||
if address != "" {
|
||||
if !strings.HasPrefix(address, "https://") && !strings.HasPrefix(address, "http://") {
|
||||
address = fmt.Sprintf("https://%s", address)
|
||||
}
|
||||
u, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return nil, errdefs.InvalidParameter(errors.Wrapf(err, "unable to parse server address"))
|
||||
}
|
||||
registryHostName = u.Host
|
||||
}
|
||||
|
||||
// Lookup endpoints for authentication using "LookupPushEndpoints", which
|
||||
// excludes mirrors to prevent sending credentials of the upstream registry
|
||||
// to a mirror.
|
||||
endpoints, err := service.LookupPushEndpoints(registryHostName)
|
||||
if err != nil {
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// loginV2 tries to login to the v2 registry server. The given registry
|
||||
// endpoint will be pinged to get authorization challenges. These challenges
|
||||
// will be used to authenticate against the registry to validate credentials.
|
||||
func loginV2(authConfig *types.AuthConfig, endpoint registry.APIEndpoint, userAgent string) (string, string, error) {
|
||||
var (
|
||||
endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/"
|
||||
modifiers = registry.Headers(userAgent, nil)
|
||||
authTransport = transport.NewTransport(newTransport(endpoint.TLSConfig), modifiers...)
|
||||
credentialAuthConfig = *authConfig
|
||||
creds = loginCredentialStore{authConfig: &credentialAuthConfig}
|
||||
)
|
||||
|
||||
logrus.Debugf("attempting v2 login to registry endpoint %s", endpointStr)
|
||||
|
||||
loginClient, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
resp, err := loginClient.Do(req)
|
||||
if err != nil {
|
||||
err = translateV2AuthError(err)
|
||||
return "", "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return "Login Succeeded", credentialAuthConfig.IdentityToken, nil
|
||||
}
|
||||
|
||||
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
|
||||
return "", "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
// newTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
|
||||
// default TLS configuration.
|
||||
func newTransport(tlsConfig *tls.Config) *http.Transport {
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = tlsconfig.ServerDefault()
|
||||
}
|
||||
|
||||
direct := &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: direct.DialContext,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
}
|
||||
|
||||
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) {
|
||||
challengeManager, _, err := registry.PingV2Registry(endpoint, authTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenHandlerOptions := auth.TokenHandlerOptions{
|
||||
Transport: authTransport,
|
||||
Credentials: creds,
|
||||
OfflineAccess: true,
|
||||
ClientID: registry.AuthClientID,
|
||||
Scopes: scopes,
|
||||
}
|
||||
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
|
||||
basicHandler := auth.NewBasicHandler(creds)
|
||||
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
|
||||
|
||||
return &http.Client{
|
||||
Transport: transport.NewTransport(authTransport, modifiers...),
|
||||
Timeout: 15 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func translateV2AuthError(err error) error {
|
||||
switch e := err.(type) {
|
||||
case *url.Error:
|
||||
switch e2 := e.Err.(type) {
|
||||
case errcode.Error:
|
||||
switch e2.Code {
|
||||
case errcode.ErrorCodeUnauthorized:
|
||||
return errdefs.Unauthorized(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func isNotAuthorizedError(err error) bool {
|
||||
return strings.Contains(err.Error(), "401 Unauthorized")
|
||||
}
|
||||
42
vendor/oras.land/oras-go/pkg/auth/docker/logout.go
vendored
Normal file
42
vendor/oras.land/oras-go/pkg/auth/docker/logout.go
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright The ORAS Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
|
||||
"oras.land/oras-go/pkg/auth"
|
||||
)
|
||||
|
||||
// Logout logs out from a docker registry identified by the hostname.
|
||||
func (c *Client) Logout(_ context.Context, hostname string) error {
|
||||
hostname = resolveHostname(hostname)
|
||||
|
||||
var configs []*configfile.ConfigFile
|
||||
for _, config := range c.configs {
|
||||
if _, ok := config.AuthConfigs[hostname]; ok {
|
||||
configs = append(configs, config)
|
||||
}
|
||||
}
|
||||
if len(configs) == 0 {
|
||||
return auth.ErrNotLoggedIn
|
||||
}
|
||||
|
||||
// Log out form the primary config only as backups are read-only.
|
||||
return c.primaryCredentialsStore(hostname).Erase(hostname)
|
||||
}
|
||||
86
vendor/oras.land/oras-go/pkg/auth/docker/resolver.go
vendored
Normal file
86
vendor/oras.land/oras-go/pkg/auth/docker/resolver.go
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright The ORAS Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/containerd/containerd/remotes"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
ctypes "github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/docker/registry"
|
||||
|
||||
iface "oras.land/oras-go/pkg/auth"
|
||||
)
|
||||
|
||||
// Resolver returns a new authenticated resolver.
|
||||
// Deprecated: use ResolverWithOpts
|
||||
func (c *Client) Resolver(_ context.Context, client *http.Client, plainHTTP bool) (remotes.Resolver, error) {
|
||||
return docker.NewResolver(docker.ResolverOptions{
|
||||
Credentials: c.Credential,
|
||||
Client: client,
|
||||
PlainHTTP: plainHTTP,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ResolverWithOpts returns a new authenticated resolver with custom options.
|
||||
func (c *Client) ResolverWithOpts(options ...iface.ResolverOption) (remotes.Resolver, error) {
|
||||
settings := &iface.ResolverSettings{}
|
||||
for _, option := range options {
|
||||
option(settings)
|
||||
}
|
||||
return docker.NewResolver(docker.ResolverOptions{
|
||||
Credentials: c.Credential,
|
||||
Client: settings.Client,
|
||||
PlainHTTP: settings.PlainHTTP,
|
||||
Headers: settings.Headers,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Credential returns the login credential of the request host.
|
||||
func (c *Client) Credential(hostname string) (string, string, error) {
|
||||
hostname = resolveHostname(hostname)
|
||||
var (
|
||||
auth ctypes.AuthConfig
|
||||
err error
|
||||
)
|
||||
for _, cfg := range c.configs {
|
||||
auth, err = cfg.GetAuthConfig(hostname)
|
||||
if err != nil {
|
||||
// fall back to next config
|
||||
continue
|
||||
}
|
||||
if auth.IdentityToken != "" {
|
||||
return "", auth.IdentityToken, nil
|
||||
}
|
||||
if auth.Username == "" && auth.Password == "" {
|
||||
// fall back to next config
|
||||
continue
|
||||
}
|
||||
return auth.Username, auth.Password, nil
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// resolveHostname resolves Docker specific hostnames
|
||||
func resolveHostname(hostname string) string {
|
||||
switch hostname {
|
||||
case registry.IndexHostname, registry.IndexName, registry.DefaultV2Registry.Host:
|
||||
return registry.IndexServer
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
Reference in New Issue
Block a user