-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck-image-exists.go
163 lines (133 loc) · 4.09 KB
/
check-image-exists.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package main
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
type TokenResponse struct {
Token string `json:"token"`
}
func fetchBearerToken(authenticateHeader, username, password string) (string, error) {
// Extract necessary info from the WWW-Authenticate header
realm := strings.Split(authenticateHeader, "realm=\"")[1]
realm = strings.Split(realm, "\"")[0]
service := strings.Split(authenticateHeader, "service=\"")[1]
service = strings.Split(service, "\"")[0]
scope := strings.Split(authenticateHeader, "scope=\"")[1]
scope = strings.Split(scope, "\"")[0]
tokenURL := fmt.Sprintf("%s?service=%s&scope=%s", realm, service, scope)
req, err := http.NewRequest("GET", tokenURL, nil)
if err != nil {
return "", err
}
// If credentials are provided, set them
if username != "" && password != "" {
req.SetBasicAuth(username, password)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", fmt.Errorf("failed to fetch bearer token, status: %s", resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var tokenResponse TokenResponse
err = json.Unmarshal(body, &tokenResponse)
if err != nil {
return "", err
}
return tokenResponse.Token, nil
}
func main() {
// Command line flags for authentication
var username, password string
flag.StringVar(&username, "user", "", "Username for Docker registry authentication")
flag.StringVar(&password, "pass", "", "Password for Docker registry authentication")
flag.Parse()
args := flag.Args()
if len(args) != 1 {
fmt.Println("You must provide the image and tag as 'image:tag' or 'registry/namespace/image:tag'.")
os.Exit(1)
}
// Split the image:tag format
imageParts := strings.Split(args[0], ":")
if len(imageParts) != 2 {
fmt.Println("Invalid format. Expected 'image:tag' or 'registry/namespace/image:tag'.")
os.Exit(1)
}
registryURL := "https://registry-1.docker.io" // default to Docker Hub
repoName, tag := imageParts[0], imageParts[1]
// If a different registry is specified, extract it from the image reference
if strings.Count(repoName, "/") > 1 {
parts := strings.SplitN(repoName, "/", 2)
registryURL = "https://" + parts[0]
repoName = parts[1]
}
url := fmt.Sprintf("%s/v2/%s/manifests/%s", registryURL, repoName, tag)
// Set headers required by the Docker registry API
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error creating request:", err)
os.Exit(1)
}
// Basic Auth, if credentials are provided
if username != "" && password != "" {
auth := username + ":" + password
encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth))
req.Header.Add("Authorization", "Basic "+encodedAuth)
}
req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
os.Exit(1)
}
defer resp.Body.Close()
// If a 401 Unauthorized response is received, try to fetch a bearer token
if resp.StatusCode == 401 {
token, err := fetchBearerToken(resp.Header.Get("WWW-Authenticate"), username, password)
if err != nil {
fmt.Println("Failed to get bearer token:", err)
os.Exit(1)
}
// Retry the request with the bearer token
req, err = http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error creating request:", err)
os.Exit(1)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
resp, err = client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
os.Exit(1)
}
defer resp.Body.Close()
}
// 200-299 statuses indicate that the tag exists
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
fmt.Println("exist")
os.Exit(0)
}
// If tag is not found, exit 4
if resp.StatusCode == 404 {
fmt.Println("noexist")
os.Exit(0)
}
// Any other status is an error
fmt.Printf("Unexpected response: %s\n", resp.Status)
os.Exit(1)
}