Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib/podex: add daemon mode #4868

Merged
merged 3 commits into from
Feb 26, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 59 additions & 22 deletions contrib/podex/podex.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ import (

const usage = "podex [-format=yaml|json] [-type=pod|container] [-id NAME] IMAGES..."

var manifestFormat = flag.String("format", "yaml", "manifest format to output, `yaml` or `json`")
var manifestType = flag.String("type", "pod", "manifest type to output, `pod` or `container`")
var manifestName = flag.String("name", "", "manifest name, default to image base name")
var flManifestFormat = flag.String("format", "yaml", "manifest format to output, `yaml` or `json`")
var flManifestType = flag.String("type", "pod", "manifest type to output, `pod` or `container`")
var flManifestName = flag.String("name", "", "manifest name, default to image base name")
var flDaemon = flag.Bool("daemon", false, "daemon mode")

func init() {
flag.Usage = func() {
Expand All @@ -66,21 +67,54 @@ type image struct {
func main() {
flag.Parse()

if *flDaemon {
http.HandleFunc("/pods/", func(w http.ResponseWriter, r *http.Request) {
image := strings.TrimPrefix(r.URL.Path, "/pods/")
_, _, manifestName, _ := splitDockerImageName(image)
manifest, err := getManifest(manifestName, "pod", "json", image)
if err != nil {
errMessage := fmt.Sprintf("failed to generate pod manifest for image %q: %v", image, err)
log.Print(errMessage)
http.Error(w, errMessage, http.StatusInternalServerError)
return
}
io.Copy(w, manifest)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}

if flag.NArg() < 1 {
flag.Usage()
log.Fatal("pod: missing image argument")
}
if *manifestName == "" {
if *flManifestName == "" {
if flag.NArg() > 1 {
flag.Usage()
log.Fatal("podex: -id arg is required when passing more than one image")
}
_, _, *manifestName, _ = splitDockerImageName(flag.Arg(0))
_, _, *flManifestName, _ = splitDockerImageName(flag.Arg(0))
}
if *flManifestType != "pod" && *flManifestType != "container" {
flag.Usage()
log.Fatalf("unsupported manifest type %q", *flManifestType)
}
if *flManifestFormat != "yaml" && *flManifestFormat != "json" {
flag.Usage()
log.Fatalf("unsupported manifest format %q", *flManifestFormat)
}

manifest, err := getManifest(*flManifestName, *flManifestType, *flManifestFormat, flag.Args()...)
if err != nil {
log.Fatalf("failed to generate %q manifest for %v: %v", *flManifestType, flag.Args(), err)
}
io.Copy(os.Stdout, manifest)
}

// getManifest infers a pod (or container) manifest for a list of docker images.
func getManifest(manifestName, manifestType, manifestFormat string, images ...string) (io.Reader, error) {
podContainers := []goyaml.MapSlice{}

for _, imageName := range flag.Args() {
for _, imageName := range images {
host, namespace, repo, tag := splitDockerImageName(imageName)

container := goyaml.MapSlice{
Expand All @@ -91,13 +125,13 @@ func main() {
img, err := getImageMetadata(host, namespace, repo, tag)

if err != nil {
log.Fatalf("failed to get image metadata %q: %v", imageName, err)
return nil, fmt.Errorf("failed to get image metadata %q: %v", imageName, err)
}
portSlice := []goyaml.MapSlice{}
for p := range img.ContainerConfig.ExposedPorts {
port, err := strconv.Atoi(p.Port())
if err != nil {
log.Fatalf("failed to parse port %q: %v", p.Port(), err)
return nil, fmt.Errorf("failed to parse port %q: %v", p.Port(), err)
}
portEntry := goyaml.MapSlice{{
Key: "name",
Expand Down Expand Up @@ -125,48 +159,47 @@ func main() {

var data interface{}

switch *manifestType {
switch manifestType {
case "container":
containerManifest = append(goyaml.MapSlice{
{Key: "id", Value: *manifestName},
{Key: "id", Value: manifestName},
}, containerManifest...)
data = containerManifest
case "pod":
data = goyaml.MapSlice{
{Key: "id", Value: *manifestName},
{Key: "id", Value: manifestName},
{Key: "kind", Value: "Pod"},
{Key: "apiVersion", Value: "v1beta1"},
{Key: "desiredState", Value: goyaml.MapSlice{
{Key: "manifest", Value: containerManifest},
}},
}
default:
flag.Usage()
log.Fatalf("unsupported manifest type %q", *manifestType)
return nil, fmt.Errorf("unsupported manifest type %q", manifestFormat)
}

yamlBytes, err := goyaml.Marshal(data)
if err != nil {
log.Fatalf("failed to marshal container manifest: %v", err)
return nil, fmt.Errorf("failed to marshal container manifest: %v", err)
}

switch *manifestFormat {
switch manifestFormat {
case "yaml":
os.Stdout.Write(yamlBytes)
return bytes.NewBuffer(yamlBytes), nil
case "json":
jsonBytes, err := yaml.YAMLToJSON(yamlBytes)
if err != nil {
log.Fatalf("failed to marshal container manifest into JSON: %v", err)
return nil, fmt.Errorf("failed to marshal container manifest into JSON: %v", err)
}
var jsonPretty bytes.Buffer
if err := json.Indent(&jsonPretty, jsonBytes, "", " "); err != nil {
log.Fatalf("failed to indent json %q: %v", string(jsonBytes), err)
return nil, fmt.Errorf("failed to indent json %q: %v", string(jsonBytes), err)
}
io.Copy(os.Stdout, &jsonPretty)
return &jsonPretty, nil
default:
flag.Usage()
log.Fatalf("unsupported manifest format %q", *manifestFormat)
return nil, fmt.Errorf("unsupported manifest format %q", manifestFormat)
}

}

// splitDockerImageName split a docker image name of the form [HOST/][NAMESPACE/]REPOSITORY[:TAG]
Expand Down Expand Up @@ -229,8 +262,12 @@ func getImageMetadata(host, namespace, repo, tag string) (*imageMetadata, error)
req.Header.Add("X-Docker-Token", "true")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("error getting X-Docker-Token from index.docker.io: %v", err)
return nil, fmt.Errorf("error making request to %q: %v", host, err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("error getting X-Docker-Token from %s: %q", host, resp.Status)
}

endpoints := resp.Header.Get("X-Docker-Endpoints")
token := resp.Header.Get("X-Docker-Token")
req, err = http.NewRequest("GET", fmt.Sprintf("https://%s/v1/repositories/%s/%s/tags/%s", endpoints, namespace, repo, tag), nil)
Expand Down