func (cmd *DestroyCommand) Execute(args []string) error {
	Logger.Info("Destroying... using information from dir:", cmd.Options.Dir)

	// Check Config and Buildfiles
	configFile, buildFile, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	dc := NewDockerApi(cmd.App.AppState.Meta, configFile, buildFile)
	dc.ShowInfo()

	// Register channel so we can watch for events as they happen
	eventsChannel := make(ApiChannel)
	go watchForEventsOn(eventsChannel)
	dc.RegisterChannel(eventsChannel)

	fqImageName := cmd.App.AppState.BuildFile.ImageName + ":" + cmd.App.AppState.BuildFile.Tag

	if _, err := dc.DeleteContainer(fqImageName); err != nil {
		Logger.Error("Could not delete container,", err)
		return err
	}

	if _, err := dc.DeleteImage(fqImageName); err != nil {
		Logger.Error("Could not delete image,", err)
		return err
	}

	msg := "Destroyed all known containers and images associated with: " + fqImageName
	Logger.Console(msg)

	// Nothing to do
	return nil
}
func (cmd *GroundCommand) Execute(args []string) error {
	Logger.Info("Grounding Tests... in dir:", cmd.Options.Dir)

	// Check Config and Buildfiles
	configFile, buildFile, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	dc := NewDockerApi(cmd.App.AppState.Meta, configFile, buildFile)
	dc.ShowInfo()

	if err := dc.createTestTemplates(*cmd.Options); err != nil {
		return err
	}

	// Register channel so we can watch for events as they happen
	eventsChannel := make(ApiChannel)
	go watchForEventsOn(eventsChannel)
	dc.RegisterChannel(eventsChannel)

	fqImageName := cmd.App.AppState.BuildFile.ImageName + ":" + cmd.App.AppState.BuildFile.Tag
	if running, err := dc.ListContainers(fqImageName); err != nil {
		Logger.Error("Error while trying to get a list of containers for ", fqImageName)
		return err
	} else {
		for _, container := range running {
			dc.StopContainer(container)
		}
	}

	Logger.Console("Grounded.")

	return nil
}
func (fc *FlightControls) CheckConfigs(app *TestFlight, options *CommandOptions) (*ConfigFile, *BuildFile, error) {
	// Setup Logging
	// TODO: Replace with only Load once app.Init() is gone
	Logger.Load(len(options.Verbose))
	Logger.Setup()

	// Set vars
	if options.Dir == "" {
		options.Dir = "./" // set to local working dir as default
	}
	Logger.Info("Using directory:", options.Dir)

	// Prereqs
	app.SetDir(options.Dir)

	configFile, err := ReadConfigFile(options.Configfile, options.Dir)
	if err != nil {
		return nil, nil, err
	}

	app.SetConfigFile(configFile)
	Logger.Info("Using configfile:", configFile.Location)

	// Get the buildfile
	// TODO: as more Control funcs get created refactor this below
	buildFile, err := fc.CheckBuild(options.Dir)
	if err != nil {
		return nil, nil, err
	}
	app.SetBuildFile(buildFile)
	Logger.Info("Using buildfile:", buildFile.Location)
	Logger.Debug("buildfile - contents:", *buildFile)

	if err := fc.CheckRequiredFiles(options.Dir, options.SingleFileMode, buildFile); err != nil {
		return nil, nil, err
	}

	return configFile, buildFile, nil
}
示例#4
0
func (cmd *CheckCommand) Execute(args []string) error {
	Logger.Info("Running Pre-Flight Check... in dir:", cmd.Options.Dir)

	// Check Config and Buildfiles
	_, b, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		Logger.Error("Could not verify config files. " + err.Error())
	} else {
		generateRequiredFilesFrom(b)

		Logger.Console("All checks passed! Files found!")
	}

	return nil
}
示例#5
0
func (api *DockerApi) ShowImages() error {
	images, _ := api.client.ListImages(true)

	if len(images) <= 0 {
		Logger.Info("No docker images found.")
		return nil
	}

	for _, img := range images {
		Logger.Info("ID: ", img.ID)
		Logger.Info("RepoTags: ", img.RepoTags)
		Logger.Info("Created: ", img.Created)
		Logger.Info("Size: ", img.Size)
		Logger.Info("VirtualSize: ", img.VirtualSize)
		Logger.Info("ParentId: ", img.ParentId)
		Logger.Info("Repository: ", img.Repository)
	}

	return nil
}
示例#6
0
func (api *DockerApi) StartContainer(id string) (*string, error) {
	endpoint := api.configFile.DockerEndpoint

	postBody := DockerHostConfig{}

	url := strings.Join(
		[]string{
			endpoint,
			"containers",
			id,
			"start",
		},
		"/",
	)
	Logger.Trace("StartContainer() - Api call to:", url)

	var jsonResult string
	bytesReader, _ := postBody.Bytes()

	resp, _ := http.Post(url, "text/json", bytes.NewReader(bytesReader))
	defer resp.Body.Close()

	if _, err := ioutil.ReadAll(resp.Body); err != nil {
		Logger.Error("Could not contact docker endpoint:", endpoint)
		return nil, err
	} else {
		switch resp.StatusCode {
		case 204:
			Logger.Info("Started container:", id)
			return &jsonResult, nil
		case 404:
			Logger.Warn("No such container")
		case 500:
			Logger.Error("Error while trying to communicate to docker endpoint:", endpoint)
		}

		msg := "Unexpected response code: " + string(resp.StatusCode)
		Logger.Error(msg)
		return nil, errors.New(msg)
	}
}
func (cmd *TemplateCommand) Execute(args []string) error {
	Logger.Info("Creating Templates... in dir:", cmd.Options.Dir)

	_, _, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	cmd.App.AppState.Meta.Dir = cmd.Options.Dir

	dc := NewDockerApi(cmd.App.AppState.Meta, cmd.App.AppState.ConfigFile, cmd.App.AppState.BuildFile)

	err = dc.createTestTemplates(*cmd.Options)

	if err != nil {
		Logger.Console("Could not create Test-Flight templates.")
		return err
	}
	Logger.Console("Test-Flight templates created.")
	return nil
}
示例#8
0
func (api *DockerApi) StopContainer(containerId string) (string, error) {
	endpoint := api.configFile.DockerEndpoint

	url := strings.Join(
		[]string{
			endpoint,
			"containers",
			containerId,
			"stop",
		},
		"/",
	)
	Logger.Trace("StopContainer() - Api call to:", url)

	resp, _ := http.Post(url, "text/json", nil)
	defer resp.Body.Close()

	if _, err := ioutil.ReadAll(resp.Body); err != nil {
		Logger.Error("Could not contact docker endpoint:", endpoint)
		return "", err
	} else {
		switch resp.StatusCode {
		case 204:
			Logger.Info("Stopped container:", containerId)
			return containerId, nil
		case 404:
			msg := "No such container"
			Logger.Warn(msg)
			return "", nil
		case 500:
			msg := "Error while trying to communicate to docker endpoint: " + endpoint
			Logger.Error(msg)
			return "", nil
		}

		msg := "Unexpected response code: " + string(resp.StatusCode)
		Logger.Error(msg)
		return "", errors.New(msg)
	}
}
func (cmd *ImagesCommand) Execute(args []string) error {
	Logger.Info("Listing images...")

	// Check Config and Buildfiles
	configFile, buildFile, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	// Api interaction here
	dc := NewDockerApi(cmd.App.AppState.Meta, configFile, buildFile)
	dc.ShowInfo()

	fqImageName := buildFile.ImageName + ":" + buildFile.Tag

	if imageDetails, err := dc.GetImageDetails(fqImageName); err != nil {
		return err
	} else if imageDetails != nil {
		imageDetails.Print()
	}

	return nil
}
示例#10
0
// == Build Command ==
// Should build a docker image
func (cmd *BuildCommand) Execute(args []string) error {
	// Set vars
	Logger.Info("Building... using information from dir:", cmd.Options.Dir)

	// Check Config and Buildfiles
	configFile, buildFile, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	// Api interaction here
	dc := NewDockerApi(cmd.App.AppState.Meta, configFile, buildFile)
	dc.ShowInfo()

	// Generate Templates
	// TODO: fails here with filemode
	if err := dc.createTestTemplates(*cmd.Options); err != nil {
		return err
	}

	// Register channel so we can watch for events as they happen
	eventsChannel := make(ApiChannel)
	go watchForEventsOn(eventsChannel)
	dc.RegisterChannel(eventsChannel)

	fqImageName := buildFile.ImageName + ":" + buildFile.Tag

	image, err := dc.CreateDockerImage(fqImageName, cmd.Options)
	if err != nil {
		return err
	}

	msg := "Created Docker Image: " + image
	Logger.Console(msg)
	return nil
}
示例#11
0
func (cmd *LaunchCommand) Execute(args []string) error {
	Logger.Info("Launching Tests... in dir:", cmd.Options.Dir)
	Logger.Debug("Force:", cmd.Options.Force)

	var wg sync.WaitGroup // used for channels

	// Check Config and Buildfiles
	configFile, buildFile, err := cmd.Controls.CheckConfigs(cmd.App, cmd.Options)
	if err != nil {
		return err
	}

	dc := NewDockerApi(cmd.App.AppState.Meta, configFile, buildFile)
	dc.ShowInfo()

	if err := dc.createTestTemplates(*cmd.Options); err != nil {
		return err
	}
	Logger.Trace("Created test-flight templates.")

	// Register channel so we can watch for events as they happen
	eventsChannel := make(ApiChannel)
	go watchForEventsOn(eventsChannel)
	dc.RegisterChannel(eventsChannel)

	fqImageName := cmd.App.AppState.BuildFile.ImageName + ":" + buildFile.Tag

	getImageId := func() (string, error) {

		if image, err := dc.GetImageDetails(fqImageName); err != nil {
			return "", err
		} else { // response from endpoint (but could be 404)
			if image == nil { // doesn't exist
				if newImage, err := dc.CreateDockerImage(fqImageName, cmd.Options); err != nil {
					return "", err
				} else {
					return newImage, nil
				}
			} else { // exists
				if cmd.Options.Force { // recreate anyway
					if newImage, err := dc.CreateDockerImage(fqImageName, cmd.Options); err != nil {
						return "", err
					} else {
						return newImage, nil
					}
				} else { // warn user
					Logger.Warn("Will be starting a container from an image which already exists.")
					return image.Id, nil
				}
			}
			return image.Id, nil
		}
	}

	createAndStartContainerFrom := func(imageId string) error {
		if container, err := dc.CreateContainer(imageId); err != nil {
			return err
		} else {
			Logger.Trace("Docker Container to start:", container.Id)
			if _, err := dc.StartContainer(container.Id); err != nil {
				return err
			} else {
				wg.Add(1)
				containerChannel := dc.Attach(container.Id)
				go watchContainerOn(containerChannel, &wg)
				wg.Wait()
				return nil
			}
		}
	}

	if imageId, err := getImageId(); err != nil {
		return err
	} else {
		msg := "Launching docker container: " + imageId
		Logger.Console(msg)

		return createAndStartContainerFrom(imageId)
	}

	return nil
}
示例#12
0
func (api *DockerApi) CreateContainer(fqImageName string) (*ApiPostResponse, error) {
	if _, err := api.DeleteContainer(fqImageName); err != nil {
		msg := "Error occured while trying to delete a container. " +
			" This attempt failed for the following reason:" + err.Error()

		Logger.Error(msg, err)
		return nil, err
	}

	endpoint := api.configFile.DockerEndpoint

	postBody := ApiPostRequest{
		Image:        fqImageName,
		OpenStdin:    true,
		AttachStdin:  false,
		AttachStdout: true,
		AttachStderr: true,
		Cmd:          api.buildFile.LaunchCmd,
	}

	url := FilePath(endpoint, "containers", "create")
	Logger.Trace("CreateContainer() - Api call to:", url)

	jsonResult := ApiPostResponse{}
	bytesReader, _ := postBody.Bytes()
	resp, _ := http.Post(url, "application/json", bytes.NewReader(bytesReader))
	defer resp.Body.Close()

	if body, err := ioutil.ReadAll(resp.Body); err != nil {
		Logger.Error("Could not contact docker endpoint:", endpoint)
		return nil, err
	} else {
		msg := "Unexpected response code: " + string(resp.StatusCode)

		switch resp.StatusCode {
		case 201:
			if err := json.Unmarshal(body, &jsonResult); err != nil {
				Logger.Error(err)
				return nil, err
			}
			Logger.Info("Created container:", fqImageName)
			return &jsonResult, nil
		case 404:
			msg = "No such container"
			Logger.Warn(msg)
		case 406:
			msg = "Impossible to attach (container not running)"
			Logger.Warn(msg)
		case 500:
			// noticed that when docker endpoint fails, it fails with just
			// status 500, no message back. Logs do show error though somewhat
			// slim details. i.e No command specified where command is really CMD :-(
			msg = "Server and/or API error while trying to communicate with docker " +
				"endpoint: " + endpoint + ". Could be malformed Dockerfile (template). " +
				"Check your remote docker endpoint logs."
			Logger.Error(msg)
		}

		return nil, errors.New(msg)
	}
}
示例#13
0
// CreateDockerImage creates a docker image by first creating the Dockerfile
// in memory and then tars it, prior to sending it along to the docker
// endpoint.
// Note: once the image creation starts the only way to know if it succeeded
//       is to query for it again.
func (api *DockerApi) CreateDockerImage(fqImageName string, options *CommandOptions) (string, error) {
	Logger.Debug("Creating Docker image by attempting to build Dockerfile: " + fqImageName)

	tmplName := "Dockerfile" // file ext of `.tmpl` is implicit, see below

	var requiredDockerFile = RequiredFile{
		Name: "Test-Flight Dockerfile", FileName: tmplName, FileType: "f",
	}

	modeDir := func() string {
		if options.SingleFileMode {
			return "filemode"
		} else {
			return "dirmode"
		}
	}()

	dockerfileBuffer := bytes.NewBuffer(nil)
	tarbuf := bytes.NewBuffer(nil)

	// Dockerfile mem
	dockerfile := bufio.NewWriter(dockerfileBuffer)
	// Or create file
	templateOutputDir := getTemplateDir(api.configFile)
	Logger.Trace(templateOutputDir, requiredDockerFile.FileName)
	fileToCreate := FilePath(templateOutputDir.FileName, requiredDockerFile.FileName)
	Logger.Debug("Trying to build:", fileToCreate)
	dockerfileOut, err := os.Create(fileToCreate)
	if err != nil {
		Logger.Error("Could not write Dockerfile", err)
		return "", err
	}
	defer dockerfileOut.Close()

	// The directory where the templates used to create inventory and playbook
	templates := func() string {
		// could have been simpler but I want to log path at this level
		var templatePath string

		if api.configFile.UseSystemDockerTemplates {
			templatePath = FilePath(api.configFile.TestFlightAssets, "templates", "system")
		} else {
			templatePath = FilePath(api.configFile.TestFlightAssets, "templates", "user")
		}

		Logger.Debug("Using template dir:", templatePath)
		return templatePath
	}()

	// This is used below in `ExecuteTemplate()`
	templateInputDir := FilePath(templates, modeDir)

	pattern := filepath.Join(templateInputDir, requiredDockerFile.FileName+"*.tmpl")
	tmpl := template.Must(template.ParseGlob(pattern))

	// In Mem
	if err := tmpl.ExecuteTemplate(dockerfile, requiredDockerFile.FileName, *api.getTemplateVar()); err != nil {
		Logger.Error("template execution: %s", err)
		return "", err
	}

	// File
	if err := tmpl.ExecuteTemplate(dockerfileOut, requiredDockerFile.FileName, *api.getTemplateVar()); err != nil {
		Logger.Error("template execution: %s", err)
		return "", err
	}

	// For in-mem flush buffer here
	dockerfile.Flush()

	// Logger.Trace("Dockerfile buffer len", dockerfileBuffer.Len())
	Logger.Trace("Dockerfile:", dockerfileBuffer.String())
	// Logger.Info("Created Dockerfile: " + fqImageName)

	currTime := time.Now()

	// Add Dockerfile to archive, break out
	tr := tar.NewWriter(tarbuf)
	tr.WriteHeader(&tar.Header{
		Name:       "Dockerfile",
		Size:       int64(dockerfileBuffer.Len()),
		ModTime:    currTime,
		AccessTime: currTime,
		ChangeTime: currTime,
	})
	tr.Write(dockerfileBuffer.Bytes())

	// Add Context to archive
	// tar test directory
	TarDirectory(tr, api.meta.Dir, api.buildFile.Ignore)
	tr.Close()

	var wg sync.WaitGroup // used for channels
	wg.Add(1)

	Logger.Trace("Building image...")
	buildChannel := make(ContainerChannel)
	// go watchContainerOn(buildChannel, &wg)
	api.BuildImage(tarbuf, fqImageName, &buildChannel, &wg)
	// go CaptureUserCancel(&buildChannel)

	select {
	case value := <-buildChannel:
		switch value {
		case "ok":
			failMsg := "Docker Image [" + fqImageName + "] failed to build."
			// We got an ok but don't know if it is legit. Possible to get no error
			// but image will be nil (successful api call but image doesn't exist)
			if image, err := api.GetImageDetails(fqImageName); err != nil || image == nil {
				return "", errors.New(failMsg)
			} else {
				Logger.Info("Successfully built Docker image: " + fqImageName)
				return image.Id, nil
			}
		case "canceled":
			msg := "User Canceled docker creation."
			Logger.Warn(msg, "User must manually stop last running container.")
			return fqImageName, errors.New(msg)
		}
	}

	wg.Wait()

	Logger.Info("Successfully built Docker image: " + fqImageName)
	return fqImageName, nil
}
示例#14
0
// Returns container name deleted, empty string if none, and an
// error (if any)
func (api *DockerApi) DeleteContainer(name string) ([]DeletedContainer, error) {
	var deletedContainers []DeletedContainer
	var returnError error

	endpoint := api.configFile.DockerEndpoint

	delete := func(id string) (*string, error) {
		baseUrl := strings.Join(
			[]string{
				endpoint,
				"containers",
				id,
			},
			"/",
		)

		params := strings.Join(
			[]string{
				"v=true",
				"force=true",
			},
			"&",
		)
		url := baseUrl + "?" + params

		Logger.Trace("DeleteContainer() Api call:", url)

		req, _ := http.NewRequest("DELETE", url, nil)
		resp, _ := http.DefaultClient.Do(req)
		defer resp.Body.Close()

		if _, err := ioutil.ReadAll(resp.Body); err != nil {
			Logger.Error("Could not contact docker endpoint:", endpoint)
			return nil, err
		} else {
			switch resp.StatusCode {
			case 204:
				Logger.Info("Container Deleted:", name, ", with ID:", id)
				return &name, nil
			case 400:
				msg := "Bad Api param supplied while trying to delete a container, notify developers."
				Logger.Error(msg)
				return nil, errors.New(msg)
			case 404:
				Logger.Warn("Container not found, nothing to delete.")
				return nil, nil
			case 406:
				msg := "Container:" + name + " - (" + id + "), is running, cannot delete."
				Logger.Warn(msg)
				return nil, errors.New(msg)
			case 500:
				msg := "Error while trying to communicate to docker endpoint:" + endpoint
				// Logger.Error(msg)
				return nil, errors.New(msg)
			}

			statusCode := strconv.Itoa(resp.StatusCode)
			msg := "API out of sync, contact developers! Status Code:" + statusCode
			return nil, errors.New(msg)
		}
	}

	if images, err := api.ListContainers(name); err != nil {
		Logger.Error("Could not get image details:", err)
		return nil, err
	} else if images != nil {
		Logger.Info("Found", len(images), "to delete...")
		for _, container := range images {
			Logger.Debug("Trying to delete", container)
			if deleted, err := delete(container); err != nil {
				returnError = err
				msg := "Could not delete container: " + err.Error()
				Logger.Error(msg)
			} else if *deleted != "" {
				deletedContainers = append(deletedContainers, DeletedContainer{*deleted, container})
			}
		}
		return deletedContainers, returnError
	} else {
		Logger.Debug("No containers found for", name)
		return deletedContainers, returnError
	}
}