2021-08-06 13:40:23 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2021-08-10 10:21:42 +00:00
|
|
|
"encoding/json"
|
2021-08-09 14:16:33 +00:00
|
|
|
"fmt"
|
2021-08-10 10:21:42 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2021-08-09 14:16:33 +00:00
|
|
|
|
|
|
|
"coopcloud.tech/abra/web"
|
2021-08-10 10:21:42 +00:00
|
|
|
"github.com/docker/distribution/reference"
|
2021-08-06 13:40:23 +00:00
|
|
|
)
|
|
|
|
|
2021-08-10 10:21:42 +00:00
|
|
|
type RawTag struct {
|
2021-08-06 13:40:23 +00:00
|
|
|
Layer string
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:21:42 +00:00
|
|
|
type RawTags []RawTag
|
2021-08-06 13:40:23 +00:00
|
|
|
|
|
|
|
var registryURL = "https://registry.hub.docker.com/v1/repositories/%s/tags"
|
|
|
|
|
2021-08-10 10:21:42 +00:00
|
|
|
func GetRegistryTags(image string) (RawTags, error) {
|
|
|
|
var tags RawTags
|
2021-08-09 14:16:33 +00:00
|
|
|
|
|
|
|
tagsUrl := fmt.Sprintf(registryURL, image)
|
|
|
|
if err := web.ReadJSON(tagsUrl, &tags); err != nil {
|
|
|
|
return tags, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tags, nil
|
2021-08-06 13:40:23 +00:00
|
|
|
}
|
2021-08-10 10:21:42 +00:00
|
|
|
|
|
|
|
// getRegv2Token retrieves a registry v2 authentication token.
|
|
|
|
func getRegv2Token(image reference.Named) (string, error) {
|
|
|
|
img := reference.Path(image.(reference.Named))
|
|
|
|
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: 10 * time.Second}
|
|
|
|
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.(reference.Named))
|
|
|
|
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: 10 * time.Second}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-08-10 11:16:41 +00:00
|
|
|
registryResT1 := struct {
|
2021-08-10 10:21:42 +00:00
|
|
|
SchemaVersion int
|
|
|
|
MediaType string
|
|
|
|
Manifests []struct {
|
|
|
|
MediaType string
|
|
|
|
Size int
|
|
|
|
Digest string
|
|
|
|
Platform struct {
|
|
|
|
Architecture string
|
|
|
|
Os string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}{}
|
|
|
|
|
2021-08-10 11:16:41 +00:00
|
|
|
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 {
|
2021-08-10 10:21:42 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
var digest string
|
2021-08-10 11:16:41 +00:00
|
|
|
for _, manifest := range registryResT1.Manifests {
|
2021-08-10 10:21:42 +00:00
|
|
|
if string(manifest.Platform.Architecture) == "amd64" {
|
|
|
|
digest = strings.Split(manifest.Digest, ":")[1][:7]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-10 11:16:41 +00:00
|
|
|
if digest == "" {
|
|
|
|
if err := json.Unmarshal(body, ®istryResT2); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
digest = strings.Split(registryResT2.Config.Digest, ":")[1][:7]
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:21:42 +00:00
|
|
|
if digest == "" {
|
|
|
|
return "", fmt.Errorf("Unable to retrieve amd64 digest for '%s'", image)
|
|
|
|
}
|
|
|
|
|
|
|
|
return digest, nil
|
|
|
|
}
|