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

registry command #536

Merged
merged 1 commit into from
Dec 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 76 additions & 39 deletions cmd/amp/registry.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package main

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"strings"

"github.com/appcelerator/amp/api/client"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/reference"
docker "github.com/docker/docker/client"
"github.com/spf13/cobra"
"golang.org/x/net/context"
"io/ioutil"
"net/http"
"regexp"
)

// RegCmd is the main command for attaching registry subcommands.
Expand All @@ -21,8 +25,10 @@ var RegCmd = &cobra.Command{
}

var (
domain = "local.appcelerator.io"
pushCmd = &cobra.Command{
endpoint string
domain string
insecure bool
pushCmd = &cobra.Command{
Use: "push [image]",
Short: "Push an image to the amp registry",
Long: `Push an image to the amp registry`,
Expand All @@ -42,53 +48,85 @@ var (

func init() {
RootCmd.AddCommand(RegCmd)
pushCmd.Flags().StringVar(&domain, "domain", domain, "The amp domain")
RegCmd.PersistentFlags().BoolVarP(&insecure, "insecure", "i", true, "Insecure registry")
RegCmd.PersistentFlags().StringVarP(&domain, "domain", "d", "local.appcelerator.io", "The amp registry domain (hostname or IP)")
RegCmd.PersistentFlags().StringVarP(&endpoint, "endpoint", "e", "", "The amp registry endpoint (hostname or IP), overrides the domain option")
RegCmd.AddCommand(pushCmd)
RegCmd.AddCommand(reglsCmd)
}

// registryEndpoint returns the registry endpoint
func registryEndpoint() (ep string) {
if endpoint != "" {
ep = endpoint
return
}
ep = "registry." + domain
return
}

// RegistryPush displays resource usage statistics
func RegistryPush(amp *client.AMP, cmd *cobra.Command, args []string) error {
_, err := amp.GetAuthorizedContext()
defaultHeaders := map[string]string{"User-Agent": "amp-cli"}
dclient, err := docker.NewClient(DockerURL, DockerVersion, nil, defaultHeaders)
if err != nil {
return err
}
ctx := context.Background()
_, err = amp.GetAuthorizedContext()
if err != nil {
return err
}
// @todo: read the .dockercfg file for authentication, or use credentials from amp.yaml
ac := types.AuthConfig{Username: "none"}
jsonString, err := json.Marshal(ac)
if err != nil {
return errors.New("Failed to marshal authconfig")
}
dst := make([]byte, base64.URLEncoding.EncodedLen(len(jsonString)))
base64.URLEncoding.Encode(dst, jsonString)
authConfig := string(dst)
imagePushOptions := types.ImagePushOptions{RegistryAuth: authConfig}

image := args[0]
distributionRef, err := distreference.ParseNamed(image)
if err != nil {
return fmt.Errorf("Error parsing reference: %q is not a valid repository/tag", image)
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
return errors.New("refusing to create a tag with a digest reference")
}
tag := reference.GetTagFromNamedRef(distributionRef)
hostname, name := distreference.SplitHostname(distributionRef)

if amp.Verbose() {
fmt.Println("Execute registry push command with:")
fmt.Printf("image: %s\n", image)
fmt.Println("Registry push request with:")
fmt.Printf(" image: %s\n", image)
}

if err = validateRegistryImage(image); err != nil {
return err
}
taggedImage := image
if !strings.HasPrefix(image, "registry."+domain) {
nn := strings.Index(image, "/")
if nn < 0 {
return fmt.Errorf("Invalid image name %s", image)
}
taggedImage = "registry." + domain + "/" + image[nn+1:]
if hostname != registryEndpoint() {
taggedImage = registryEndpoint() + "/" + name + ":" + tag
fmt.Printf("Tag image from %s to %s\n", image, taggedImage)
cmdexe := exec.Command("docker", "tag", image, taggedImage)
cmdexe.Stdout = os.Stdout
cmdexe.Stderr = os.Stderr
err = cmdexe.Run()
if err != nil {
if err := dclient.ImageTag(ctx, image, taggedImage); err != nil {
return err
}
}
fmt.Printf("push image %s\n", taggedImage)
cmdexe := exec.Command("docker", "push", taggedImage)
cmdexe.Stdout = os.Stdout
cmdexe.Stderr = os.Stderr
err = cmdexe.Run()
resp, err := dclient.ImagePush(ctx, taggedImage, imagePushOptions)
if err != nil {
return err
}
return err
body, err := ioutil.ReadAll(resp)
if err != nil {
return err
}
re := regexp.MustCompile(`: digest: sha256:`)
if !re.Match(body) {
fmt.Print(string(body))
return errors.New("Push failed")
}
return nil
}

// RegistryLs lists images
Expand All @@ -97,7 +135,13 @@ func RegistryLs(amp *client.AMP, cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
resp, err := http.Get("http://registry." + domain + "/v2/_catalog")
var protocol string
if insecure {
protocol = "http"
} else {
protocol = "https"
}
resp, err := http.Get(protocol + "://" + registryEndpoint() + "/v2/_catalog")
if err != nil {
return err
}
Expand All @@ -106,10 +150,3 @@ func RegistryLs(amp *client.AMP, cmd *cobra.Command, args []string) error {
fmt.Println(string(body))
return err
}

func validateRegistryImage(image string) error {
if image == "" {
return errors.New("Need a valid image name")
}
return nil
}
9 changes: 8 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AMP: A... Microservices Platform ![WIP](./static_files/amp--docs-WIP-yellow.svg)
AMP: A... Microservices Platform ![WIP](static_files/amp--docs-WIP-yellow.svg)
============================

AMP is currently under development and this section is here to help you get started based on latest stable tagged version of the project. If you're here, then you **ARE** pioneering with us and we encourage you to [get in touch](./contributing.md) !
Expand Down Expand Up @@ -207,3 +207,10 @@ A few useful examples:
```
$ amp stats --task --cpu --mem
```

### Managing Docker images

amp registry ls
amp registry push

More details in the [userguide](userguide/registry.md)
56 changes: 56 additions & 0 deletions docs/userguide/registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
AMP registry
============

# Usage

AMP comes with an internal registry available through the AMP cli and providing images to the Swarm cluster. It is meant to host the Docker images used by the application services (defined in a stack).

## CLI

### localhost (development)

#### Push an image

```amp registry push appcelerator/pinger:latest```

### remote cluster

the cluster should have a FQDN with sub level aliases. Let's say the domain is amp.example.com, the registry is available on registry.amp.example.com.

If there's no legit certificate for this registry with this name (default use case), this url should be declared as insecure registry in your Docker configuration.

#### Configuration on Linux

```systemctl edit docker.service```

add the block (or adapt the existing file if you already have a customization)

```
[Service]
Environment="INSECURE_REGISTRY=registry.amp.example.com"
ExecStart=-
ExecStart=/usr/bin/dockerd $OPTIONS \
$INSECURE_REGISTRY
```

#### Configuration on Mac OS

Go in Preferences, advanced tab, add an insecure registry.

#### Push an image

```amp registry push --domain amp.example.com appcelerator/pinger:latest```

#### Check the registry catalog

```amp registry ls --domain amp.example.com```

## Stack definition

The internal registry images are available with the local alias local.appcelerator.io.
In the stack definition, use this alias in the hostname part of the image:

```
myservice1:
image: local.appcelerator.io/pinger:latest
```