Last active
August 29, 2015 14:19
-
-
Save hex108/721d47ba9a3a2fc901fb to your computer and use it in GitHub Desktop.
A tool for two purposes: 1. Check repositories to find those broken ones. 2. Draw repository's image dependence using dot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"net/http" | |
"encoding/json" | |
"strings" | |
"container/list" | |
"os" | |
) | |
type Repository struct { | |
Name string | |
Description string | |
} | |
// the result of /v1/search | |
type Repositories struct{ | |
Num_results int | |
Results []Repository | |
} | |
// Get a list of repositories | |
func getRepositories(hostUrl string) (repos Repositories, err error) { | |
body, err := getConent(hostUrl + "/v1/search") | |
if err != nil { | |
fmt.Errorf("Failed to get repositories from " + hostUrl) | |
return | |
} | |
err = json.Unmarshal(body, &repos) | |
if err != nil { | |
fmt.Errorf("Failed to unmarshal " + string(body)) | |
return | |
} | |
return | |
} | |
// Check all repositories, and find broken repositories | |
func checkRepositories(hostUrl string, repos Repositories) { | |
// record whether image exists | |
imageRecords := make(map[string]bool) | |
for i := 0; i < repos.Num_results; i ++ { | |
repoName := repos.Results[i].Name | |
fmt.Println("Checking repository : " + repoName) | |
tagsInfo, err := getRepositoryTags(hostUrl, repoName) | |
if err != nil { | |
fmt.Errorf("Failed to get tags for " + repoName) | |
continue | |
} | |
for tagName, imageId := range tagsInfo { | |
fmt.Print("--> " + repoName + ":" + tagName + " : ") | |
if checkImage(hostUrl, imageId, imageRecords) { | |
fmt.Println("OK") | |
} else { | |
fmt.Println("Broken") | |
} | |
} | |
} | |
fmt.Println("Done.") | |
} | |
// check whether a image and its ancestries exist | |
func checkImage(hostUrl string, imageId string, imageRecords map[string]bool) bool{ | |
value, present := imageRecords[imageId] | |
if present { | |
return value | |
} | |
ancestry, err := getImageAncestry(hostUrl, imageId) | |
if err != nil { | |
fmt.Errorf("Failed to get ancestry: " + imageId) | |
return false | |
} | |
for _, ancestryImageId := range ancestry { | |
if ancestryImageId == imageId { | |
continue | |
} | |
//fmt.Println("Checking ancestry : " + ancestryImageId) | |
ok := checkImage(hostUrl, ancestryImageId, imageRecords) | |
// record it to avoid repeated check for a same image | |
imageRecords[ancestryImageId] = ok | |
if !ok { | |
return false | |
} | |
} | |
return true | |
} | |
func printRepositories(hostUrl string, repos Repositories) { | |
for i := 0; i < repos.Num_results; i ++ { | |
repoName := repos.Results[i].Name | |
fmt.Println("Repository : " + repoName) | |
tagsInfo, err := getRepositoryTags(hostUrl, repoName) | |
if err != nil { | |
fmt.Errorf("Failed to get tags for " + repoName) | |
continue | |
} | |
for tagName, imageId := range tagsInfo { | |
fmt.Println("tag : " + tagName + ", images ID: " + imageId) | |
//getImageList(imageId) | |
ancestry, err := getImageAncestry(hostUrl, imageId) | |
if err != nil { | |
fmt.Errorf("Failed to get ancestry: " + imageId) | |
continue | |
} | |
for _, image := range ancestry { | |
fmt.Println("--> " + image) | |
} | |
} | |
} | |
} | |
// GET /v1/repositories/(namespace)/(repository)/tags | |
func getRepositoryTags(hostUrl string, repo string) (tags map[string]string, err error) { | |
body, err := getConent(hostUrl + "/v1/repositories/" + repo + "/tags") | |
if err != nil { | |
fmt.Errorf("Failed to get tags for " + repo) | |
return | |
} | |
tags = make(map[string]string) | |
json.Unmarshal(body, &tags) | |
if err != nil { | |
fmt.Errorf("Failed to unmarshal " + string(body)) | |
return | |
} | |
return | |
} | |
// GET /v1/images/(image_id)/ancestry | |
func getImageAncestry(hostUrl string, imageId string) (ancestry []string, err error) { | |
ancestryInfo, err := getConent(hostUrl + "/v1/images/" + imageId + "/ancestry") | |
if err != nil { | |
fmt.Println("Failed to get ancestry for " + imageId) | |
return nil, err | |
} | |
err = json.Unmarshal(ancestryInfo, &ancestry) | |
if err != nil { | |
//fmt.Println("Failed to unmarshal " + string(ancestryInfo)) | |
return nil, err | |
} | |
return | |
} | |
// Draw repository's image dependence using dot | |
func drawRepository(hostUrl string, repositoryWithTag string, outFile string) { | |
imageId, err := getImageIdForTag(hostUrl, repositoryWithTag) | |
if err != nil { | |
fmt.Errorf("Failed to get image id for repository : " + repositoryWithTag) | |
return | |
} | |
out, err := os.Create(outFile) | |
if err != nil { | |
fmt.Errorf("Failed to create " + outFile) | |
return | |
} | |
defer out.Close() | |
writeHead(out, repositoryWithTag) | |
writeBody(out, repositoryWithTag, imageId[0:9]) | |
imageVisited := make(map[string]bool) | |
imagesToDraw := list.New() | |
imagesToDraw.PushBack(imageId) | |
for imagesToDraw.Len() != 0 { | |
curElement := imagesToDraw.Remove(imagesToDraw.Front()) | |
curImageId := curElement.(string) | |
_, visited := imageVisited[curImageId] | |
if visited { | |
continue | |
} else { | |
imageVisited[curImageId] = true | |
} | |
ancestry, err := getImageAncestry(hostUrl, curImageId) | |
if err != nil { | |
fmt.Errorf("Failed to get ancestry for " + curImageId) | |
return | |
} | |
for _, ancestryImage := range ancestry { | |
_, visited := imageVisited[ancestryImage] | |
if ancestryImage != curImageId && !visited { | |
imagesToDraw.PushBack(ancestryImage) | |
fmt.Println(curImageId + " -> " + ancestryImage) | |
writeBody(out, curImageId[0:9], ancestryImage[0:9]) | |
} | |
} | |
} | |
writeTail(out) | |
} | |
func writeHead(file *os.File, grahName string) { | |
file.WriteString("digraph \"" + grahName + "\" {\n") | |
file.WriteString(" node [shape = rect]\n") | |
} | |
func writeBody(file *os.File, node1 string, node2 string) { | |
file.WriteString(" \"" + node1 + "\" -> \"" + node2 + "\";\n") | |
} | |
func writeTail(file *os.File) { | |
file.WriteString("}\n") | |
} | |
// Get image id for a particular tag | |
// GET /v1/repositories/(namespace)/(repository)/tags/(tag*) | |
func getImageIdForTag(hostUrl string, repositoryWithTag string) (imageId string, err error) { | |
if !strings.Contains(repositoryWithTag, ":") { | |
repositoryWithTag += ":latest" | |
} | |
tmp := strings.Split(repositoryWithTag, ":") | |
repository, tag := tmp[0], tmp[1] | |
// TODO: need deal with library? | |
content, err := getConent(hostUrl + "/v1/repositories/" + repository + "/tags/" + tag) | |
if err != nil{ | |
fmt.Errorf("Failed to get image id for tag : " + repositoryWithTag) | |
return | |
} | |
err = json.Unmarshal(content, &imageId) | |
if err != nil { | |
fmt.Errorf("Failed to unmarshal " + string(content)) | |
return | |
} | |
return | |
} | |
// Get the body content by 'GET' | |
func getConent(url string) (data []byte, err error) { | |
resp, err := http.Get(url) | |
if err != nil { | |
fmt.Errorf("Failed to get content from" + url) | |
return nil, err | |
} | |
defer resp.Body.Close() | |
data, err = ioutil.ReadAll(resp.Body) | |
if err != nil { | |
fmt.Errorf("Failed to read body of " + url) | |
return nil, err | |
} | |
return data, nil | |
} | |
func printUsage() { | |
flag.PrintDefaults() | |
} | |
func main() { | |
var hostUrl,repositoryWithTag,outfile string | |
var checkAll bool | |
flag.BoolVar(&checkAll, "check", false, "Check all repositories") | |
flag.StringVar(&repositoryWithTag, "draw", "", "Draw repository's image dependence using dot") | |
flag.StringVar(&hostUrl, "host", "registry.oa.com:80", "Registry host url, e.g: docker.oa.com:8080") | |
flag.StringVar(&outfile, "o", "test.dot", "Generate dot file for drawing repository's image dependence") | |
flag.Bool("help", false, "Print help message") | |
flag.Parse() | |
if !strings.HasPrefix(hostUrl, "http://"){ | |
hostUrl = "http://" + hostUrl | |
} | |
if checkAll { | |
repos, err := getRepositories(hostUrl) | |
if err != nil { | |
fmt.Errorf("Failed to get repositories from " + hostUrl) | |
} else { | |
// printRepositories(hostUrl, repos) | |
checkRepositories(hostUrl, repos) | |
} | |
} else if repositoryWithTag != "" { | |
fmt.Println("Draw images ...") | |
drawRepository(hostUrl, repositoryWithTag, outfile) | |
} else { | |
printUsage() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment