Skip to content

Commit

Permalink
Added ability to push local images to clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasjackson committed Jan 7, 2020
1 parent e22e2ee commit 82c43b3
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 42 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ jobs:
- name: Build Application
run: |
GIT_COMMIT=$(git log -1 --pretty=format:"%H")
CGO_ENABLED=0 GOOS=${{matrix.OS}} GOARCH=${{matrix.ARCH}} go build -ldflags "-X main.version=${GIT_COMMIT}" -o bin/yard-${{ matrix.OS }}-${{ matrix.ARCH }} main.go
CGO_ENABLED=0 GOOS=${{matrix.OS}} GOARCH=${{matrix.ARCH}} go build -ldflags "-X main.version=${{ github.tag }}" -o bin/yard-${{ matrix.OS }}-${{ matrix.ARCH }} main.go
- name: Stash binary
uses: actions/upload-artifact@v1
Expand Down
3 changes: 1 addition & 2 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"context"

getter "github.com/hashicorp/go-getter"
"github.com/hashicorp/go-hclog"
"github.com/shipyard-run/shipyard/pkg/shipyard"
"github.com/spf13/cobra"
)
Expand All @@ -41,7 +40,7 @@ var applyCmd = &cobra.Command{
fmt.Println("")

// create a logger
log := hclog.New(&hclog.LoggerOptions{Level: hclog.Debug, Color: hclog.AutoColor})
log := createLogger()

if !IsLocalFolder(dst) {
// fetch the remote server from github
Expand Down
3 changes: 1 addition & 2 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"os"

"github.com/hashicorp/go-hclog"
"github.com/shipyard-run/shipyard/pkg/shipyard"
"github.com/spf13/cobra"
)
Expand All @@ -16,7 +15,7 @@ var deleteCmd = &cobra.Command{
Example: `yard delete my-stack`,
Run: func(cmd *cobra.Command, args []string) {

log := hclog.New(&hclog.LoggerOptions{Level: hclog.Debug, Color: hclog.AutoColor})
log := createLogger()

// When destroying a stack all the config
// which is created with apply is copied
Expand Down
54 changes: 54 additions & 0 deletions cmd/push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cmd

import (
"github.com/spf13/cobra"
"github.com/shipyard-run/shipyard/pkg/clients"
"github.com/shipyard-run/shipyard/pkg/config"
"github.com/shipyard-run/shipyard/pkg/providers"
"fmt"
"os"
"strings"
)

var pushCmd = &cobra.Command{
Use: "push [image] [cluster] [network]",
Short: "Push a local Docker image to a cluster",
Long: `Push a local Docker image to a cluster`,
Example: `yard push nicholasjackson/fake-service:v0.1.3 k3s cloud`,
DisableFlagsInUseLine: true,
Args: cobra.MaximumNArgs(3),
Run: func(cmd *cobra.Command, args []string) {
// TODO this needs validation

image := args[0]
cluster := args[1]
network := args[2]

fmt.Printf("Pushing image %s to cluster %s\n\n", image, cluster)

pc := &config.Cluster{
Name: cluster,
Driver: "k3s",
NetworkRef: &config.Network{Name: network},
}

dc,err := clients.NewDocker()
if err != nil {
fmt.Println("Error pushing image: ", err)
os.Exit(1)
}

p:= providers.NewCluster(
pc,
dc,
nil,
createLogger(),
)

err = p.ImportLocalDockerImages([]config.Image{config.Image{Name: strings.Trim(image, " ")}})
if err != nil {
fmt.Println("Error pushing image: ", err)
os.Exit(1)
}
},
}
9 changes: 5 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/spf13/viper"
)

var config = ""
var configFile = ""

var rootCmd = &cobra.Command{
Use: "yard",
Expand All @@ -27,7 +27,7 @@ var engine *shipyard.Engine
func init() {
cobra.OnInitialize(configure)

rootCmd.PersistentFlags().StringVar(&config, "config", "", "config file (default is $HOME/.shipyard/config)")
rootCmd.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.shipyard/config)")

rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(applyCmd)
Expand All @@ -40,12 +40,13 @@ func init() {
rootCmd.AddCommand(toolsCmd)
rootCmd.AddCommand(upgradeCmd)
rootCmd.AddCommand(uninstallCmd)
rootCmd.AddCommand(pushCmd)
}

func configure() {
if config != "" {
if configFile != "" {
// Use config file from the flag.
viper.SetConfigFile(config)
viper.SetConfigFile(configFile)
} else {
// Find home directory.
home, err := homedir.Dir()
Expand Down
5 changes: 5 additions & 0 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"strings"
"runtime"
"github.com/hashicorp/go-hclog"
)

// HomeFolder returns the users homefolder this will be $HOME on windows and mac and
Expand Down Expand Up @@ -61,3 +62,7 @@ func GetBlueprintFolder(blueprint string) (string, error) {

return parts[1], nil
}

func createLogger() hclog.Logger {
return hclog.New(&hclog.LoggerOptions{Level: hclog.Debug, Color: hclog.AutoColor})
}
46 changes: 34 additions & 12 deletions pkg/providers/cluster_k3s.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,33 @@ func (c *Cluster) createK3s() error {
// import the images to the servers container d instance
// importing images means that k3s does not need to pull from a remote docker hub
if c.config.Images != nil && len(c.config.Images) > 0 {
imageFile, err := writeLocalDockerImageToVolume(c.client, c.config.Images, volID, c.log)
if err != nil {
return err
}
return c.ImportLocalDockerImages(c.config.Images)
}

// import the image
//ctr image import filename
err = execCommand(c.client, id, []string{"ctr", "image", "import", imageFile})
if err != nil {
return err
}
return nil
}

// ImportLocalDockerImages fetches Docker images stored on the local client and imports them into the cluster
func (c*Cluster) ImportLocalDockerImages(images []config.Image) error {
vn := volumeName(c.config.Name)
c.log.Debug("Writing local Docker images to cluster", "ref", c.config.Name, "images", images, "volume", vn)

imageFile, err := writeLocalDockerImageToVolume(c.client, images, vn, c.log)
if err != nil {
return err
}

id, err := c.Lookup()
if err != nil {
return err
}

// import the image
// ctr image import filename
c.log.Debug("Importing Docker images on cluster", "ref", c.config.Name, "id", id, "image", imageFile)
err = execCommand(c.client, id, []string{"ctr", "image", "import", imageFile}, c.log.With("parent_ref", c.config.Name))
if err != nil {
return err
}

return nil
Expand Down Expand Up @@ -258,14 +274,20 @@ func (c *Cluster) createDockerKubeConfig(kubeconfig string) error {
}

func (c *Cluster) destroyK3s() error {
c.log.Info("Delete Cluster", "ref", c.config.Name)
c.log.Info("Destroy Cluster", "ref", c.config.Name)

cc := &config.Container{}
cc.Name = fmt.Sprintf("server.%s", c.config.Name)
cc.NetworkRef = c.config.NetworkRef

cp := NewContainer(cc, c.client, c.log.With("parent_ref", c.config.Name))
return cp.Destroy()
err := cp.Destroy()
if err != nil {
return err
}

// delete the volume
return c.deleteVolume()
}

const clusterNameMaxSize int = 35
Expand Down
17 changes: 12 additions & 5 deletions pkg/providers/cluster_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,31 @@ import (
// createVolume creates a Docker volume for a cluster
// returns the volume name and an error if unsuccessful
func (c *Cluster) createVolume() (string, error) {

name := fmt.Sprintf("%s.volume", c.config.Name)
vn :=volumeName(c.config.Name)
c.log.Debug("Create Volume", "ref", c.config.Name, "name", vn)

volumeCreateOptions := volume.VolumeCreateBody{
Name: name,
Name: vn,
Driver: "local", //TODO: allow setting driver + opts
DriverOpts: map[string]string{},
}

vol, err := c.client.VolumeCreate(context.Background(), volumeCreateOptions)
if err != nil {
return "", fmt.Errorf("failed to create image volume [%s] for cluster [%s]\n%+v", name, c.config.Name, err)
return "", fmt.Errorf("failed to create image volume [%s] for cluster [%s]\n%+v", vn, c.config.Name, err)
}

return vol.Name, nil
}

// deleteVolume deletes the Docker volume associated with a cluster
func (c *Cluster) deleteVolume() {
func (c *Cluster) deleteVolume() error {
vn := volumeName(c.config.Name)
c.log.Debug("Deleting Volume", "ref", c.config.Name, "name", vn)

return c.client.VolumeRemove(context.Background(), vn, true)
}

func volumeName(clusterName string) string {
return fmt.Sprintf("%s.volume", clusterName)
}
2 changes: 1 addition & 1 deletion pkg/providers/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (c *Container) Destroy() error {
return nil
}

return c.client.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true})
return c.client.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
}

// Lookup the containers ID based on the config
Expand Down
25 changes: 13 additions & 12 deletions pkg/providers/container_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func writeLocalDockerImageToVolume(c clients.Docker, images []config.Image, volu
}

// execute a command in a container
func execCommand(c clients.Docker, container string, command []string) error {
func execCommand(c clients.Docker, container string, command []string, l hclog.Logger) error {
id, err := c.ContainerExecCreate(context.Background(), container, types.ExecConfig{
Cmd: command,
WorkingDir: "/",
Expand All @@ -206,18 +206,19 @@ func execCommand(c clients.Docker, container string, command []string) error {
return xerrors.Errorf("unable to create container exec: %w", err)
}

// to get logs from an attach
/*
stream, err := c.ContainerExecAttach(context.Background(), id.ID, types.ExecStartCheck{})
if err != nil {
return xerrors.Errorf("unable to start exec process: %w", err)
}
defer stream.Close()
// get logs from an attach
stream, err := c.ContainerExecAttach(context.Background(), id.ID, types.ExecStartCheck{})
if err != nil {
return xerrors.Errorf("unable to attach logging to exec process: %w", err)
}
defer stream.Close()

go func() {
io.Copy(os.Stderr, stream.Reader)
}()
*/
go func() {
io.Copy(
l.StandardWriter(&hclog.StandardLoggerOptions{}),
stream.Reader,
)
}()

err = c.ContainerExecStart(context.Background(), id.ID, types.ExecStartCheck{})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/providers/k8s_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (c *K8sConfig) Create() error {

// Destroy the Kubernetes resources defined by the config
func (c *K8sConfig) Destroy() error {
c.log.Info("Delete Kubernetes configuration", "ref", c.config.Name, "config", c.config.Paths)
c.log.Info("Destroy Kubernetes configuration", "ref", c.config.Name, "config", c.config.Paths)

err := c.setup()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/providers/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (n *Network) Create() error {

// Destroy implements the provider interface method for destroying networks
func (n *Network) Destroy() error {
n.log.Info("Destroying Network", "ref", n.config.Name)
n.log.Info("Destroy Network", "ref", n.config.Name)

return n.client.NetworkRemove(context.Background(), n.config.Name)
}
Expand Down

0 comments on commit 82c43b3

Please sign in to comment.