func (api *DockerApi) Destroy(fqImageName string) error { if running, err := api.ListContainers(fqImageName); err != nil { Logger.Trace("Error while trying to get a list of containers for ", fqImageName) return err } else { for _, container := range running { if id, err := api.StopContainer(container); err != nil { Logger.Error("Could not stop container", id, " associated with", fqImageName) return err } } // Once all stopped, delete all 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 err } if _, err := api.DeleteImage(fqImageName); err != nil { Logger.Error("Could not delete image,", err) return err } } return nil }
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 }
// We only want to attach to containers associated with a particular // Image name func (api *DockerApi) Attach(containerId string) ContainerChannel { out := make(ContainerChannel) // channel to send back go CaptureUserCancel(&out, nil) go func() { endpoint := api.configFile.DockerEndpoint baseUrl := strings.Join( []string{ endpoint, "containers", containerId, "attach", }, "/", ) params := strings.Join( []string{ "stdout=true", "stderr=true", "stream=true", }, "&", ) url := baseUrl + "?" + params Logger.Trace("Attach() - Api call to:", url) // jsonResult := "" byteData := []byte{} bytesReader := bytes.NewReader(byteData) resp, err := http.Post(url, "text/json", bytesReader) defer resp.Body.Close() if err != nil { Logger.Error("Could not submit request, err:", err) } reader := bufio.NewReader(resp.Body) for { if line, err := reader.ReadBytes('\n'); err != nil { if err == io.EOF { break } else { msg := "Error reading from stream on attached container, error:" + err.Error() Logger.Error(msg) // close(out) // return error.New(msg) } } else { Logger.ConsoleChannel(string(bytes.TrimSpace(line)[:])) } } close(out) }() return out }
// ListContainers that are running func (api *DockerApi) ListContainers(imageName string) ([]string, error) { endpoint := api.configFile.DockerEndpoint baseUrl := strings.Join( []string{ endpoint, "containers", "json", }, "/", ) params := strings.Join( []string{ "all=true", }, "&", ) url := baseUrl + "?" + params Logger.Trace("ListContainers Api call:", url) resp, _ := http.Get(url) 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 { switch resp.StatusCode { case 200: var jsonResult []ApiContainer if err := json.Unmarshal(body, &jsonResult); err != nil { Logger.Error("Error while trying to marshall result, body:", jsonResult, " - Error:", err) return nil, err } else { var ids []string for i := range jsonResult { if jsonResult[i].Image == imageName { ids = append(ids, jsonResult[i].Id) } } Logger.Debug("Containers found for", imageName, ids) return ids, nil } case 404: Logger.Warn("Bad param supplied to API") case 500: Logger.Error("Error while trying to communicate to docker endpoint:", endpoint) } } return nil, 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 (api *DockerApi) GetImageDetails(fqImageName string) (*ApiDockerImage, error) { url := strings.Join( []string{ api.configFile.DockerEndpoint, "images", fqImageName, "json", }, "/", ) // For testing, also need to `import "encoding/json"` // result := map[string]json.RawMessage{} result := ApiDockerImage{} Logger.Trace("GetImageDetails Api call to:", url) if resp, err := napping.Get(url, nil, &result, nil); err != nil { Logger.Error("Error while getting Image information from docker,", err) return nil, err } else { switch resp.Status() { case 200: Logger.Trace(result) return &result, nil case 404: Logger.Debug("Image not found") return nil, nil } return nil, nil } }
func (api *DockerApi) RegisterChannel(eventsChannel chan *docker.APIEvents) { if err := api.client.AddEventListener(eventsChannel); err != nil { Logger.Error(err) } eventsChannel <- &docker.APIEvents{Status: "Listening in on Docker Events..."} }
func (bf *BuildFile) ParseJson(data []byte) error { if err := json.Unmarshal(data, &bf); err != nil { Logger.Error("Could not parse json build file file.", err) return err } return nil }
// tries to find config file in user home, then if it cannot find one there // will try to find a config file in the local running directory func findConfig(dir string) (*ConfigFile, error) { configFileName := Constants().configFileName configFile := NewConfigFile() logConfigFile := func(configFile *ConfigFile) { Logger.Debug("Found config file.") Logger.Trace("Config file contents:", *configFile) } // try dir specified localConfigPath := FilePath(dir, configFileName) configFile, err := getConfig(localConfigPath) if err != nil { msg := "Config: " + localConfigPath + " may not exist or cannot be read. " + err.Error() Logger.Debug(msg) } else { logConfigFile(configFile) return configFile, nil } // try running directory next pwd, err := os.Getwd() if err != nil { return nil, err } Logger.Debug("Checking for config file in local pwd: " + pwd + "/" + configFileName) pwdConfigPath := FilePath(pwd, ".test-flight", configFileName) configFile, err = getConfig(pwdConfigPath) if err != nil { msg := "Config: " + localConfigPath + " may not exist or cannot be read. " + err.Error() Logger.Debug(msg) } else { logConfigFile(configFile) return configFile, nil } // try home usr, err := user.Current() // to get user home, get user first if err != nil { Logger.Error("Can't read user home.") // return nil, ReadFileError.New("Can't read user home.") return nil, errors.New("Can't read user home.") } homeConfigPath := FilePath(usr.HomeDir, ".test-flight", configFileName) Logger.Debug("Checking for config file in user HOME: ", homeConfigPath) configFile, err = getConfig(homeConfigPath) if err != nil { msg := "Config: " + localConfigPath + " may not exist or cannot be read. " + err.Error() Logger.Debug(msg) } else { logConfigFile(configFile) return configFile, nil } return nil, errors.New("Cannot find config file, Please supply the config file.") }
func (bf *BuildFile) ParseYaml(data []byte) error { if err := yaml.Unmarshal(data, &bf); err != nil { Logger.Error("Could not parse yaml build file.", err) return err } fmt.Println("**", bf) return nil }
// Runs Test-Flight func main() { if _, err := parser.Parse(); err != nil { // Exclude flags.Error case because `--help` will return an error // https://github.com/jessevdk/go-flags/issues/45 if _, ok := err.(*flags.Error); !ok { // type assertion returns (var,bool) Logger.Error(err) os.Exit(lib.ExitCodes["command_fail"]) } } }
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 (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 (app *TestFlight) Init(meta *ApplicationMeta) error { app.AppState.Meta = meta execPath, error := osext.Executable() if error != nil { msg := "Could not find executable path." Logger.Error(msg) return errors.New(msg) } // app.AppState.Meta.ExecPath = execPath meta.ExecPath = execPath pwd, error := os.Getwd() if error != nil { msg := "Could not find working directory." Logger.Error(msg) return errors.New(msg) } meta.Pwd = pwd return nil }
func ReadBuildFile(filePath string) (*BuildFile, error) { jsonBlob, _ := ioutil.ReadFile(filePath) var buildFile = NewBuildFile() err := json.Unmarshal(jsonBlob, buildFile) if err != nil { Logger.Error("Error while trying to parse buildfile,", filePath, err) return nil, err } buildFile.Location = filePath return buildFile, nil }
// http get using golang packaged lib func (api *DockerApi) ShowImageGo() (*ApiDockerImage, error) { endpoint := api.configFile.DockerEndpoint url := strings.Join( []string{ endpoint, "images", api.buildFile.ImageName + ":" + api.buildFile.Tag, "json", }, "/", ) resp, _ := http.Get(url) 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 { switch resp.StatusCode { case 200: var jsonResult ApiDockerImage if err := json.Unmarshal(body, &jsonResult); err != nil { Logger.Error(err) return nil, err } return &jsonResult, nil case 404: Logger.Debug("Image not found") case 500: Logger.Error("Error while trying to communicate to docker endpoint:", endpoint) } return nil, nil } }
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 }
func NewDockerApi(meta *ApplicationMeta, configFile *ConfigFile, buildFile *BuildFile) *DockerApi { api := DockerApi{ meta: meta, configFile: configFile, buildFile: buildFile, } client, err := docker.NewClient(configFile.DockerEndpoint) if err != nil { Logger.Error("Docker API Client Error:", err) os.Exit(ExitCodes["docker_error"]) } Logger.Debug("Docker client created using endpoint:", configFile.DockerEndpoint) api.client = client return &api }
// Generates required files // Note: complex types are free form and can be aything // where simple types are top level only. func generateRequiredFilesFrom(buildfile *BuildFile) ([]RequiredFile, error) { requiredFiles := []RequiredFile{} for _, file := range buildfile.Add.Simple { name := "Ansible " + file + " dir" requiredFiles = append(requiredFiles, RequiredFile{Name: name, FileName: file, FileType: "d" /*,RequiredFiles: AnsibleFiles*/}) } // Treat Complex as fully qualified paths and as OS Files for _, file := range buildfile.Add.Complex { if file.Name == "" || file.Location == "" { msg := "Found empty values for " + Constants().buildFileName + " Complex entry. Check syntax, try again." Logger.Error(msg) return nil, errors.New(msg) } name := "file " + file.Name requiredFiles = append(requiredFiles, RequiredFile{Name: name, FileName: file.Location, FileType: "f"}) } Logger.Trace(requiredFiles) return requiredFiles, nil }
// 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 } }
func TarDirectory(tw *tar.Writer, dir string, ignoreList []string) error { Logger.Trace("Taring: ", dir) // Add Dockerfile to the ignore list // This can also help prevent existing Dockerfiles from being used // and accidently shipped in the context. ignoreList = append(ignoreList, "Dockerfile") shouldIgnore := func(file string) bool { for _, entry := range ignoreList { if entry == file { return true } } return false } var archive = func(files []os.FileInfo) error { Logger.Trace("Found files to archive into context: ", len(files)) for _, file := range files { if shouldIgnore(file.Name()) { Logger.Debug("Ignoring:", file.Name()) continue } fullFilePath := strings.Join([]string{dir, file.Name()}, "/") if file.IsDir() { TarDirectory(tw, fullFilePath, ignoreList) continue } hdr := &tar.Header{ Name: fullFilePath, Size: file.Size(), } if err := tw.WriteHeader(hdr); err != nil { Logger.Error("Could not write context archive header", err) return err } if bytes, err := ioutil.ReadFile(fullFilePath); err != nil { Logger.Error("Could not read context file: ["+fullFilePath+"]", err) return err } else { if _, err := tw.Write(bytes); err != nil { Logger.Error("Could not archive context file: ["+fullFilePath+"]", err) return err } } Logger.Trace("Archived into context the file: [" + fullFilePath + "]") } Logger.Trace("Successfully archived context", dir) return nil } if filesFromDisk, err := ioutil.ReadDir(dir); err != nil { Logger.Error("Error while trying to tar ["+dir+"]", err) return err } else { return archive(filesFromDisk) } }
func (api *DockerApi) BuildImage( buffer *bytes.Buffer, imageName string, watch *ContainerChannel, wg *sync.WaitGroup, ) { // out := make(ContainerChannel) // channel to send back go CaptureUserCancel(watch, wg) go func() error { endpoint := api.configFile.DockerEndpoint baseUrl := strings.Join( []string{ endpoint, "build", }, "/", ) params := strings.Join( []string{ "t=" + imageName, }, "&", ) url := baseUrl + "?" + params Logger.Trace("BuildImage() - Api call to:", url) type BuildStream struct { Stream string } var jsonResult BuildStream bytesReader := bytes.NewReader(buffer.Bytes()) resp, err := http.Post(url, "application/tar", bytesReader) defer resp.Body.Close() if err != nil { Logger.Error("Could not submit request, err:", err) return err } reader := bufio.NewReader(resp.Body) for { if line, err := reader.ReadBytes('\n'); err != nil { if err == io.EOF { break } else { msg := "Error reading from stream on building image, error:" + err.Error() Logger.Error(msg) *watch <- "ok" return errors.New(msg) } } else { if err := json.Unmarshal(line, &jsonResult); err != nil { Logger.Error(err) } else { Logger.ConsoleChannel(strings.TrimSpace(jsonResult.Stream)) } } } *watch <- "ok" wg.Done() return nil }() }
func (api *DockerApi) DeleteImage(name string) (string, error) { type ContainerStatus map[string]string status := func(statusMap ContainerStatus) string { possibleStatus := []string{"Untagged", "Deleted"} for _, v := range possibleStatus { containerId := statusMap[v] if containerId != "" { return containerId } } Logger.Error("Api out of sync, could not map status, using UNKNOWN") return "UNKNOWN" } endpoint := api.configFile.DockerEndpoint delete := func(imageId string) ([]string, error) { var deletedContainers []string baseUrl := strings.Join( []string{ endpoint, "images", imageId, }, "/", ) params := strings.Join( []string{ "force=true", "noprune=false", }, "&", ) url := baseUrl + "?" + params Logger.Trace("DeleteImage() Api call:", url) req, _ := http.NewRequest("DELETE", url, nil) resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() if body, err := ioutil.ReadAll(resp.Body); err != nil { Logger.Error("Could not contact docker endpoint:", endpoint) } else { switch resp.StatusCode { case 200: var jsonResult []ContainerStatus if err := json.Unmarshal(body, &jsonResult); err != nil { Logger.Error(err) } else { for _, v := range jsonResult { deletedContainers = append(deletedContainers, status(v)) } return deletedContainers, nil } case 409: msg := "Cannot delete image while in use by a container. Delete the container first." Logger.Warn(msg) return deletedContainers, nil case 404: msg := "Image not found, cannot delete." Logger.Warn(msg) return deletedContainers, nil case 500: msg := "Error while trying to communicate to docker endpoint:" Logger.Error(msg) return nil, errors.New(msg) } msg := "API Out of sync, contact developers. Response code: " + strconv.Itoa(resp.StatusCode) Logger.Error(msg) return nil, errors.New(msg) } return deletedContainers, nil } // Need funcs.map(_.apply) where funcs is type [](func, errorString) if image, err := api.GetImageDetails(name); err != nil { Logger.Error("Could not get image details:", err) return "", nil } else if image != nil && err == nil { if _, err := delete(image.Id); err != nil { Logger.Error("Could not delete image: ", name, ", Id:", image.Id, ", Error was:", err) return "", err } return name, nil } return "", nil }
// 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 }
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) } }
func (api *DockerApi) createTestTemplates(options CommandOptions) error { var templateDir = getTemplateDir(api.configFile) var inventory = RequiredFile{ Name: "Test-Flight Test Inventory file", FileName: "inventory", FileType: "f", } var playbook = RequiredFile{ Name: "Test-Flight Test Playbook file", FileName: "playbook.yml", FileType: "f", } // TODO: better to have a type specified that is calculated // much earlier in the process var modeDir = func() string { if options.SingleFileMode { return "filemode" } else { return "dirmode" } }() // --HERE MUST KNOW ABOUT SUB DIR dirmode/filemode of templates // -- needs to work for input and output dir // 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("Template dir used for ansible tests:", templatePath) return templatePath }() // This is used below in `ExecuteTemplate()` templateInputDir := FilePath(templates, modeDir) Logger.Debug("Template dir used for generation of ansible files:", templateInputDir) // The directory where to put the generated files templateOutputDir := FilePath(api.meta.Pwd /*api.meta.Dir,*/, templateDir.FileName) Logger.Debug("Template dir used to put generated ansible test files:", templateOutputDir) createFilesFromTemplate := func( templateInputDir string, templateOutputDir string, requiredFile RequiredFile) error { hasFiles, _ := HasRequiredFile(templateOutputDir, requiredFile) if hasFiles && api.configFile.OverwriteTemplates || !hasFiles { fileToCreate := FilePath(templateOutputDir, requiredFile.FileName) var err error file, err := os.Create(fileToCreate) if err != nil { Logger.Error("Error:", err) return err } pattern := filepath.Join(templateInputDir, requiredFile.FileName+"*.tmpl") tmpl := template.Must(template.ParseGlob(pattern)) if err = tmpl.ExecuteTemplate(file, requiredFile.FileName, *api.getTemplateVar()); err != nil { Logger.Error("template execution: %s", err) return err } Logger.Debug("Created file from template:", fileToCreate) } else if hasFiles && !api.configFile.OverwriteTemplates { Logger.Debug(requiredFile.Name, "exists, and system configured to not overwrite.") } return nil } // If the test-flight templates dir doesn't exist, create it. hasFiles, err := HasRequiredFile(api.meta.Dir, templateDir) if !hasFiles { // create if it doesn't exist if _, err = CreateFile(&api.meta.Dir, templateDir); err != nil { return err } } else { Logger.Debug("Required template files already exist. Overwrite set to:", api.configFile.OverwriteTemplates) } err = createFilesFromTemplate(templateInputDir, templateOutputDir, inventory) if err != nil { return err } err = createFilesFromTemplate(templateInputDir, templateOutputDir, playbook) if err != nil { return err } return nil }