docker

package
v0.0.0-...-deba36d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2023 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Manager = schema.ModuleManager{
	Name:            "docker",
	ModulePrototype: &Docker{},
	StatePrototype:  &state{},
	GobRegister: func() {
		snobgob.Register(&startDockerRegistryAndSSHTunnelCommand{})
		snobgob.Register(&dockerTagPushCommand{})
		snobgob.Register(&containerCommand{})
		snobgob.Register(&removeImagesCommand{})
		snobgob.Register(&installDockerCommand{})
		snobgob.Register(&writeCronCommand{})
	},
	GetState: func(query interface{}) (interface{}, error) {
		state := &state{Installed: true}

		client, err := getClient()
		if err != nil {
			if err == notInstalledErr {
				state.Installed = false
				return state, nil
			}

			return nil, fmt.Errorf("Could not get docker client from environment. Details: %v", err.Error())
		}

		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		containers, err := client.ContainerList(ctx, types.ContainerListOptions{All: true})
		if err != nil {
			return nil, fmt.Errorf("Could not list Docker Containers. Error: %v", err)
		}
		state.Containers = containers

		images, err := client.ImageList(context.Background(), types.ImageListOptions{All: true})
		if err != nil {
			return nil, fmt.Errorf("Could not list Docker Images. Error: %v", err.Error())
		}
		state.Images = images

		state.Cron = []byte{}
		if _, err := os.Stat(cronFile); err == nil {
			cronfileBytes, err := ioutil.ReadFile(cronFile)
			if err != nil {
				return nil, fmt.Errorf("Could not read cron file (%v). Error: %v", cronFile, err.Error())
			}
			state.Cron = cronfileBytes
		} else if !os.IsNotExist(err) {
			return nil, fmt.Errorf("Could not read cron file (%v). Error: %v", cronFile, err.Error())
		}

		return state, nil
	},
	CalculateCommands: func(c *schema.CalculateCommandsArgs) error {
		remoteState := c.State.(*state)
		modules := c.Modules.([]*Docker)

		cronCommands := make([]string, 0)

		if len(modules) == 0 && !remoteState.Installed {
			return nil
		}

		if len(modules) == 0 {
			return nil
		}

		remoteRoot := c.RemoteCommands.AsCommand()
		if !remoteState.Installed {
			remoteRoot = remoteRoot.Add("Install Docker", &installDockerCommand{}).AsCommand()
		}

		client, err := getClient()
		var localImages []types.ImageSummary
		func() {

			defer func() {
				if r := recover(); r != nil {
					err = fmt.Errorf("Could not communicate with docker deamon. (details: panic: %v)", r)
				}
			}()
			localImages, err = client.ImageList(context.Background(), types.ImageListOptions{All: true})
		}()
		if err != nil {
			return neaterror.New(map[string]interface{}{
				"error message": err.Error(),
			}, "Could not list local Docker images. Is Docker installed and running?")
		}

		localImageMap := make(map[string]types.ImageSummary)
		localImageUsage := make(map[string]bool)
		for _, img := range localImages {
			localImageMap[img.ID] = img
			localImageUsage[img.ID] = false
		}

		remoteImageMap := make(map[string]types.ImageSummary)
		for _, img := range remoteState.Images {
			remoteImageMap[img.ID] = img
		}

		rootPushCommand := &startDockerRegistryAndSSHTunnelCommand{connection: c.RemoteConnection}
		pushCommands := make(map[string]*commandtree.Command)
		containerNames := make(map[string]bool)
		for _, module := range modules {

			tag, err := module.Image.Render(nil)
			if err != nil {
				return err
			}
			containerName, err := module.Name.Render(nil)
			if err != nil {
				return err
			}
			folder, err := module.Folder.Render(nil)
			if err != nil {
				return err
			}
			if folder != "" {
				tag = filepath.Base(folder) + ":latest"
			}
			cron, err := module.Cron.Render(nil)
			if err != nil {
				return err
			}
			cronUser, err := module.CronUser.Render(nil)
			if err != nil {
				return err
			}

			// find the corresponding image currently on the local machine.
			var localImage types.ImageSummary
			found := false
			for _, image := range localImages {
				for _, imageTag := range image.RepoTags {
					if imageTag == tag {
						localImage = image
						found = true
						break
					}
				}
			}
			if !found {
				list := make([]string, 0)
				for _, image := range localImages {
					for _, imageTag := range image.RepoTags {
						if imageTag != "<none>:<none>" {
							list = append(list, imageTag)
						}
					}
				}
				return fmt.Errorf("Could not locate image tag '%v' on this machine. Are you sure it's built? Images found: %v", tag, list)
			}

			l := localImage
			for true {
				localImageUsage[l.ID] = true

				if l.ParentID != "" {
					if parent, found := localImageMap[l.ParentID]; !found {
						return fmt.Errorf("Could not find image %v locally. This shouldn't ever happpen", l.ParentID)
					} else {
						l = parent
					}
				} else {
					break
				}
			}

			_, alreadyInRemote := remoteImageMap[localImage.ID]
			_, markedForPush := pushCommands[tag]
			if !alreadyInRemote && !markedForPush {
				pushCommands[tag] = rootPushCommand.AsCommand().Add("Push "+tag, &dockerTagPushCommand{
					client:  client,
					imageID: localImage.ID,
					tag:     tag,
				}).AsCommand()
				if len(pushCommands) == 1 {
					c.LocalCommands.Add("Push Docker Images", rootPushCommand)
				}
			}

			command, err := module.Command.Render(nil)
			if err != nil {
				return err
			}
			options := make([]string, 0, len(module.Options))
			for _, t := range module.Options {
				opt, err := t.Render(nil)
				if err != nil {
					return err
				}
				options = append(options, opt)
			}

			if cron != "" {
				if containerName != "" {
					return fmt.Errorf("Containers running under Cron should not have a name defined")
				}

				pullTag := ""
				if !alreadyInRemote {
					pullTag = fmt.Sprintf("127.0.0.1:%v/%v", registryPort, tag)
				}
				if pullTag != "" {
					remoteRoot.Add("Docker Image: "+pullTag, &containerCommand{
						PullTag: pullTag,
					})
				}

				if cronUser == "" {
					cronUser = "root"
				}

				cmd := bytes.NewBuffer(nil)
				cmd.WriteString(cron)
				cmd.WriteString("\t")
				cmd.WriteString(cronUser)
				cmd.WriteString("\t")
				cmd.WriteString("docker run")
				cmd.WriteString(" --rm")
				for _, opt := range options {
					cmd.WriteString(" ")
					cmd.WriteString(opt)
				}
				cmd.WriteString(" " + localImage.ID)
				cmd.WriteString(" " + command)
				cronCommands = append(cronCommands, cmd.String())
			} else {

				h := sha1.New()
				h.Write([]byte(command))
				h.Write([]byte(localImage.ID))
				h.Write([]byte(folder))
				for _, option := range options {
					h.Write([]byte(option))
				}
				containerVersion := fmt.Sprintf("%x", h.Sum(nil))

				if containerName == "" {
					return fmt.Errorf("Container must have a name. ")
				}
				if _, found := containerNames[containerName]; found {
					return fmt.Errorf("the container name '%v' is used more than once", containerName)
				}
				containerNames[containerName] = true

				startContainer := true
				stopID := ""

				for _, container := range remoteState.Containers {
					for _, n := range container.Names {
						if n == "/"+containerName {
							stopID = container.ID
							if value, found := container.Labels["dogo"]; found {
								if value == containerVersion {
									if container.State == "running" {
										startContainer = false
									} else {
										c.Logf("will start %v because its state is '%v'", containerName, container.State)
									}
								}
							}
						}
					}
				}

				if startContainer {
					pullTag := ""
					if !alreadyInRemote {
						pullTag = fmt.Sprintf("127.0.0.1:%v/%v", registryPort, tag)
					}

					cmd := bytes.NewBuffer(nil)
					cmd.WriteString("docker run")
					cmd.WriteString(" --detach")
					cmd.WriteString(" --label dogo=" + containerVersion)
					cmd.WriteString(" --name " + containerName)
					for _, opt := range options {
						cmd.WriteString(" ")
						cmd.WriteString(opt)
					}
					cmd.WriteString(" " + localImage.ID)
					cmd.WriteString(" " + command)
					remoteRoot.Add("Docker Container: "+containerName, &containerCommand{
						PullTag:         pullTag,
						StopContainerID: stopID,
						StartCommand:    cmd.String(),
					})
				}
			}
		}

		for _, img := range remoteState.Images {
			if used, found := localImageUsage[img.ID]; !found || !used {
				usedInContainer := false
				for _, container := range remoteState.Containers {
					if container.ImageID == img.ID {
						usedInContainer = true
						break
					}
				}
				if !usedInContainer {

				}
			}
		}

		buf := bytes.NewBuffer(nil)
		for _, command := range cronCommands {
			buf.WriteString(command)
			buf.WriteString("\n")
		}
		arr := buf.Bytes()
		if !bytes.Equal(arr, remoteState.Cron) {
			if len(arr) == 0 {
				remoteRoot.Add("Remove "+cronFile, &writeCronCommand{Content: arr})
			} else {
				remoteRoot.Add("Update "+cronFile, &writeCronCommand{Content: arr})
			}
		}

		return nil
	},
}

Manager is the main entry point to this Dogo Module

Functions

func StartDockerRegistry

func StartDockerRegistry(logLevel string) error

Types

type Docker

type Docker struct {
	// which image to run
	Folder schema.Template
	Image  schema.Template

	// If running as a cron job
	Cron     schema.Template
	CronUser schema.Template

	// how the container should be configured.
	Name    schema.Template `required:"yes" description:"The container name to use."`
	Command schema.Template
	Options []schema.Template
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL