diff --git a/pkg/apiserver/registries/registries.go b/pkg/apiserver/registries/registries.go index fd19b90f7..e58a33c7f 100644 --- a/pkg/apiserver/registries/registries.go +++ b/pkg/apiserver/registries/registries.go @@ -25,8 +25,8 @@ import ( "kubesphere.io/kubesphere/pkg/errors" "kubesphere.io/kubesphere/pkg/models/registries" - log "github.com/golang/glog" k8serror "k8s.io/apimachinery/pkg/api/errors" + log "k8s.io/klog" ) func RegistryVerify(request *restful.Request, response *restful.Response) { @@ -51,21 +51,19 @@ func RegistryVerify(request *restful.Request, response *restful.Response) { } func RegistryImageBlob(request *restful.Request, response *restful.Response) { - var statusUnauthorized = "Not found or unauthorized" - imageName := request.QueryParameter("image") namespace := request.QueryParameter("namespace") secretName := request.QueryParameter("secret") // get entry entry, err := registries.GetEntryBySecret(namespace, secretName) - if err != nil && k8serror.IsNotFound(err) { - log.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response) - return - } if err != nil { log.Errorf("%+v", err) + if k8serror.IsNotFound(err) { + log.Errorf("%+v", err) + errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response) + return + } response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) return } @@ -97,15 +95,15 @@ func RegistryImageBlob(request *restful.Request, response *restful.Response) { } // Get digest. - imageManifest, err, statusCode := r.ImageManifest(image, token) - if statusCode == http.StatusUnauthorized { - log.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(statusCode, statusUnauthorized), response) - return - } + imageManifest, err := r.ImageManifest(image, token) if err != nil { + if serviceError, ok := err.(restful.ServiceError); ok { + response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: serviceError.Message}) + return + } log.Errorf("%+v", err) response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) + return } image.Digest = imageManifest.ManifestConfig.Digest diff --git a/pkg/models/registries/blob.go b/pkg/models/registries/blob.go index fec305537..1b281381f 100644 --- a/pkg/models/registries/blob.go +++ b/pkg/models/registries/blob.go @@ -4,15 +4,16 @@ import ( "encoding/json" "fmt" "github.com/docker/distribution/manifest/schema2" - log "github.com/golang/glog" + log "k8s.io/klog" "net/http" ) // Digest returns the digest for an image. func (r *Registry) ImageBlob(image Image, token string) (*ImageBlob, error) { - + if image.Path == "" { + return nil, fmt.Errorf("image is required") + } url := r.GetBlobUrl(image) - log.Info("registry.blobs.get url=" + url) req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -32,14 +33,14 @@ func (r *Registry) ImageBlob(image Image, token string) (*ImageBlob, error) { respBody, _ := GetRespBody(resp) if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { - log.Info("got response: " + string(resp.StatusCode) + string(respBody)) - return nil, fmt.Errorf("got response: %s", respBody) + log.Error("got response: " + string(resp.StatusCode) + string(respBody)) + return nil, fmt.Errorf("got image blob faild") } imageBlob := &ImageBlob{} - json.Unmarshal(respBody, imageBlob) + err = json.Unmarshal(respBody, imageBlob) - return imageBlob, nil + return imageBlob, err } func (r *Registry) GetBlobUrl(image Image) string { diff --git a/pkg/models/registries/manifest.go b/pkg/models/registries/manifest.go index 639760177..57d76f58e 100644 --- a/pkg/models/registries/manifest.go +++ b/pkg/models/registries/manifest.go @@ -6,17 +6,19 @@ import ( "net/http" "github.com/docker/distribution/manifest/schema2" - log "github.com/golang/glog" + "github.com/emicklei/go-restful" + log "k8s.io/klog" ) +var statusUnauthorized = "Not found or unauthorized" + // Digest returns the digest for an image. -func (r *Registry) ImageManifest(image Image, token string) (*ImageManifest, error, int) { +func (r *Registry) ImageManifest(image Image, token string) (*ImageManifest, error) { url := r.GetDigestUrl(image) - log.Info("registry.manifests.get url=" + url) req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, err, http.StatusInternalServerError + return nil, err } req.Header.Add("Accept", schema2.MediaTypeManifest) @@ -26,21 +28,25 @@ func (r *Registry) ImageManifest(image Image, token string) (*ImageManifest, err resp, err := r.Client.Do(req) if err != nil { - return nil, err, http.StatusInternalServerError + return nil, err } defer resp.Body.Close() respBody, _ := GetRespBody(resp) - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { - log.Info("got response: " + string(resp.StatusCode) + string(respBody)) - return nil, fmt.Errorf("%s", respBody), resp.StatusCode + if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusUnauthorized { + log.Error(statusUnauthorized) + return nil, restful.NewError(resp.StatusCode, statusUnauthorized) + } + log.Error("got response: " + string(resp.StatusCode) + string(respBody)) + return nil, restful.NewError(resp.StatusCode, "got image manifest failed") } imageManifest := &ImageManifest{} - json.Unmarshal(respBody, imageManifest) + err = json.Unmarshal(respBody, imageManifest) - return imageManifest, nil, http.StatusOK + return imageManifest, err } func (r *Registry) GetDigestUrl(image Image) string { diff --git a/pkg/models/registries/manifest_test.go b/pkg/models/registries/manifest_test.go index 289d390b9..7afad1cce 100644 --- a/pkg/models/registries/manifest_test.go +++ b/pkg/models/registries/manifest_test.go @@ -20,7 +20,7 @@ func TestDigestFromDockerHub(t *testing.T) { t.Fatalf("Could not get token: %s", err) } - d, err, _ := r.ImageManifest(testImage, token) + d, err := r.ImageManifest(testImage, token) if err != nil { t.Fatalf("Could not get digest: %s", err) } diff --git a/pkg/models/registries/registries.go b/pkg/models/registries/registries.go index 9cee977f0..93fa62a7b 100644 --- a/pkg/models/registries/registries.go +++ b/pkg/models/registries/registries.go @@ -25,8 +25,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/golang/glog" - log "github.com/golang/glog" corev1 "k8s.io/api/core/v1" + log "k8s.io/klog" "kubesphere.io/kubesphere/pkg/informers" ) @@ -103,7 +103,7 @@ func getDockerEntryFromDockerSecret(instance *corev1.Secret) (dockerConfigEntry if instance.Type != corev1.SecretTypeDockerConfigJson { return nil, fmt.Errorf("secret %s in ns %s type should be %s", - instance.Namespace, instance.Name, corev1.SecretTypeDockerConfigJson) + instance.Name, instance.Namespace, corev1.SecretTypeDockerConfigJson) } dockerConfigBytes, ok := instance.Data[corev1.DockerConfigJsonKey] if !ok { diff --git a/pkg/models/registries/registry_client.go b/pkg/models/registries/registry_client.go index 5f729f4e6..5db8fde65 100644 --- a/pkg/models/registries/registry_client.go +++ b/pkg/models/registries/registry_client.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" "github.com/docker/docker/api/types" - log "github.com/golang/glog" "io" "io/ioutil" + log "k8s.io/klog" "net/http" "net/url" "regexp" @@ -19,6 +19,8 @@ const ( // DefaultDockerRegistry is the default docker registry address. DefaultDockerRegistry = "https://registry-1.docker.io" + DefaultDockerHub = "docker.io" + DefaultTimeout = 30 * time.Second ) @@ -31,8 +33,6 @@ var ( ErrBasicAuth = errors.New("basic auth required") gcrMatcher = regexp.MustCompile(`https://([a-z]+\.|)gcr\.io/`) - - reProtocol = regexp.MustCompile("^https?://") ) // Registry defines the client for retrieving information from the registry API. @@ -42,11 +42,11 @@ type Registry struct { Username string Password string Client *http.Client - Opt Opt + Opt RegistryOpt } // Opt holds the options for a new registry. -type Opt struct { +type RegistryOpt struct { Domain string Timeout time.Duration Headers map[string]string @@ -68,14 +68,12 @@ func CreateRegistryClient(username, password, domain string) (*Registry, error) authDomain := domain auth, err := GetAuthConfig(username, password, authDomain) if err != nil { + log.Error(err) return nil, err } // Create the registry client. - log.Infof("domain: %s", domain) - log.Infof("server address: %s", auth.ServerAddress) - - return New(auth, Opt{ + return New(auth, RegistryOpt{ Domain: domain, }) } @@ -83,7 +81,7 @@ func CreateRegistryClient(username, password, domain string) (*Registry, error) // GetAuthConfig returns the docker registry AuthConfig. func GetAuthConfig(username, password, registry string) (types.AuthConfig, error) { registry = setDefaultRegistry(registry) - if username != "" && password != "" && registry != "" { + if username != "" && password != "" { return types.AuthConfig{ Username: username, Password: password, @@ -91,8 +89,6 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error }, nil } - log.Info("Using registry ", registry, " with no authentication") - return types.AuthConfig{ ServerAddress: registry, }, nil @@ -100,39 +96,31 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error } func setDefaultRegistry(serverAddress string) string { - if serverAddress == "docker.io" || serverAddress == "" { + if serverAddress == DefaultDockerHub || serverAddress == "" { serverAddress = DefaultDockerRegistry } return serverAddress } -func newFromTransport(auth types.AuthConfig, opt Opt) (*Registry, error) { - if len(opt.Domain) < 1 || opt.Domain == "docker.io" { +func newFromTransport(auth types.AuthConfig, opt RegistryOpt) (*Registry, error) { + if len(opt.Domain) < 1 || opt.Domain == DefaultDockerHub { opt.Domain = auth.ServerAddress } - url := strings.TrimSuffix(opt.Domain, "/") - authURL := strings.TrimSuffix(auth.ServerAddress, "/") + registryUrl := strings.TrimSuffix(opt.Domain, "/") - if !reProtocol.MatchString(url) { + if !strings.HasPrefix(registryUrl, "http://") && !strings.HasPrefix(registryUrl, "https://") { if opt.UseSSL { - url = "https://" + url + registryUrl = "https://" + registryUrl } else { - url = "http://" + url - } - } - - if !reProtocol.MatchString(authURL) { - if opt.UseSSL { - authURL = "https://" + authURL - } else { - authURL = "http://" + authURL + registryUrl = "http://" + registryUrl } } + registryURL, _ := url.Parse(registryUrl) registry := &Registry{ - URL: url, - Domain: reProtocol.ReplaceAllString(url, ""), + URL: registryURL.String(), + Domain: registryURL.Host, Client: &http.Client{ Timeout: DefaultTimeout, }, @@ -152,7 +140,7 @@ func (r *Registry) url(pathTemplate string, args ...interface{}) string { } // New creates a new Registry struct with the given URL and credentials. -func New(auth types.AuthConfig, opt Opt) (*Registry, error) { +func New(auth types.AuthConfig, opt RegistryOpt) (*Registry, error) { return newFromTransport(auth, opt) } diff --git a/pkg/models/registries/registry_client_test.go b/pkg/models/registries/registry_client_test.go new file mode 100644 index 000000000..ad7da03fa --- /dev/null +++ b/pkg/models/registries/registry_client_test.go @@ -0,0 +1,61 @@ +package registries + +import ( + "testing" +) + +func TestCreateRegistryClient(t *testing.T) { + type imageInfo struct { + Username string + Password string + Domain string + ExDomain string + ExUrl string + } + + testImages := []imageInfo{ + {Domain: "kubesphere.io", ExDomain: "kubesphere.io", ExUrl: "http://kubesphere.io"}, + {Domain: "127.0.0.1:5000", ExDomain: "127.0.0.1:5000", ExUrl: "http://127.0.0.1:5000"}, + {Username: "Username", Password: "Password", Domain: "docker.io", ExDomain: "registry-1.docker.io", ExUrl: "https://registry-1.docker.io"}, + {Domain: "harbor.devops.kubesphere.local:30280", ExDomain: "harbor.devops.kubesphere.local:30280", ExUrl: "http://harbor.devops.kubesphere.local:30280"}, + } + + for _, testImage := range testImages { + reg, err := CreateRegistryClient(testImage.Username, testImage.Password, testImage.Domain) + if err != nil { + t.Fatalf("Get err %s", err) + } + + if reg.Domain != testImage.ExDomain { + t.Fatalf("Doamin got %v, expected %v", reg.Domain, testImage.ExDomain) + } + + if reg.URL != testImage.ExUrl { + t.Fatalf("URL got %v, expected %v", reg.URL, testImage.ExUrl) + } + + } + + testImage := Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"} + r, err := CreateRegistryClient("", "", "docker.io") + if err != nil { + t.Fatalf("Could not get client: %s", err) + } + + digestUrl := r.GetDigestUrl(testImage) + + // Get token. + token, err := r.Token(digestUrl) + if err != nil || token == "" { + t.Fatalf("Could not get token: %s", err) + } + + d, err := r.ImageManifest(testImage, token) + if err != nil { + t.Fatalf("Could not get digest: %s", err) + } + + if d == nil { + t.Error("Empty digest received") + } +} diff --git a/pkg/models/registries/token.go b/pkg/models/registries/token.go index c3b01473a..3a37e4666 100644 --- a/pkg/models/registries/token.go +++ b/pkg/models/registries/token.go @@ -7,8 +7,6 @@ import ( "net/http" "net/url" "strings" - - log "github.com/golang/glog" ) func (t authToken) String() (string, error) { @@ -52,7 +50,6 @@ func isTokenDemand(resp *http.Response) (*authService, error) { // Token returns the required token for the specific resource url. If the registry requires basic authentication, this // function returns ErrBasicAuth. func (r *Registry) Token(url string) (str string, err error) { - log.Info("Get token from ", url) req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err @@ -72,17 +69,16 @@ func (r *Registry) Token(url string) (str string, err error) { return "", ErrBasicAuth } - a, err := isTokenDemand(resp) + authService, err := isTokenDemand(resp) if err != nil { return "", err } - if a == nil { - log.Info("registry.token authService=nil") + if authService == nil { return "", nil } - authReq, err := a.Request(r.Username, r.Password) + authReq, err := authService.Request(r.Username, r.Password) if err != nil { return "", err }