package client import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strings" "coopcloud.tech/abra/web" "github.com/docker/distribution/reference" ) type RawTag struct { Layer string Name string } type RawTags []RawTag var registryURL = "https://registry.hub.docker.com/v1/repositories/%s/tags" func GetRegistryTags(image string) (RawTags, error) { var tags RawTags tagsUrl := fmt.Sprintf(registryURL, image) if err := web.ReadJSON(tagsUrl, &tags); err != nil { return tags, err } return tags, nil } // getRegv2Token retrieves a registry v2 authentication token. func getRegv2Token(image reference.Named) (string, error) { img := reference.Path(image) authTokenURL := fmt.Sprintf("https://auth.docker.io/token?service=registry.docker.io&scope=repository:%s:pull", img) req, err := http.NewRequest("GET", authTokenURL, nil) if err != nil { return "", err } client := &http.Client{Timeout: web.Timeout} res, err := client.Do(req) if err != nil { return "", err } defer res.Body.Close() if res.StatusCode != http.StatusOK { _, err := ioutil.ReadAll(res.Body) if err != nil { return "", err } } body, err := ioutil.ReadAll(res.Body) if err != nil { return "", nil } tokenRes := struct { Token string Expiry string Issued string }{} if err := json.Unmarshal(body, &tokenRes); err != nil { return "", err } return tokenRes.Token, nil } // GetTagDigest retrieves an image digest from a v2 registry func GetTagDigest(image reference.Named) (string, error) { img := reference.Path(image) tag := image.(reference.NamedTagged).Tag() manifestURL := fmt.Sprintf("https://index.docker.io/v2/%s/manifests/%s", img, tag) req, err := http.NewRequest("GET", manifestURL, nil) if err != nil { return "", err } token, err := getRegv2Token(image) if err != nil { return "", err } req.Header = http.Header{ "Accept": []string{ "application/vnd.docker.distribution.manifest.v2+json", "application/vnd.docker.distribution.manifest.list.v2+json", }, "Authorization": []string{fmt.Sprintf("Bearer %s", token)}, } client := &http.Client{Timeout: web.Timeout} res, err := client.Do(req) if err != nil { return "", err } defer res.Body.Close() if res.StatusCode != http.StatusOK { _, err := ioutil.ReadAll(res.Body) if err != nil { return "", err } } body, err := ioutil.ReadAll(res.Body) if err != nil { return "", err } registryResT1 := struct { SchemaVersion int MediaType string Manifests []struct { MediaType string Size int Digest string Platform struct { Architecture string Os string } } }{} registryResT2 := struct { SchemaVersion int MediaType string Config struct { MediaType string Size int Digest string } Layers []struct { MediaType string Size int Digest string } }{} if err := json.Unmarshal(body, ®istryResT1); err != nil { return "", err } var digest string for _, manifest := range registryResT1.Manifests { if string(manifest.Platform.Architecture) == "amd64" { digest = strings.Split(manifest.Digest, ":")[1][:7] } } if digest == "" { if err := json.Unmarshal(body, ®istryResT2); err != nil { return "", err } digest = strings.Split(registryResT2.Config.Digest, ":")[1][:7] } if digest == "" { return "", fmt.Errorf("Unable to retrieve amd64 digest for '%s'", image) } return digest, nil }