func (g *Gaudi) copyRelativeFiles(filePath, destination string) bool { // File cannot be absolute if util.IsFile(filePath) && filePath[0] == '/' { util.LogError("File '" + filePath + "' cannot be an absolute path") } // Check if the relative file exists absolutePath := g.ApplicationDir + "/" + filePath if util.IsFile(absolutePath) { // Move file to the build context (and keep the same file tree) directories := strings.Split(filePath, "/") if len(directories) > 1 { os.MkdirAll(destination+strings.Join(directories[0:len(directories)-1], "/"), 0755) } err := util.Copy(destination+filePath, absolutePath) if err != nil { util.LogError(err) } return true } return false }
func (gaudi *Gaudi) parseTemplate(sourceDir, destinationDir string, file os.FileInfo, includes map[string]string, currentContainer *container.Container) { emptyCmd := shouldEmptyCmdForContainer(currentContainer.Name) templateData := TemplateData{gaudi.All, nil, emptyCmd} funcMap := template.FuncMap{ "ToUpper": strings.ToUpper, "ToLower": strings.ToLower, } // Create destination directory if needed destination := destinationDir + currentContainer.Name + "/" + file.Name() if file.IsDir() { err := os.MkdirAll(destination, 0755) if err != nil { util.LogError(err) } return } // Read the template filePath := sourceDir + "/" + file.Name() rawContent, err := ioutil.ReadFile(filePath) if err != nil { util.LogError(err) } content := string(rawContent) // Add includes for name, include := range includes { content = strings.Replace(content, "[[ "+name+" ]]", include, -1) } // Parse it // We need to change default delimiters because sometimes we have to parse values like ${{{ .Val }}} which cause an error tmpl, templErr := template.New(filePath).Funcs(funcMap).Delims("[[", "]]").Parse(content) if templErr != nil { util.LogError(templErr) } templateData.Container = currentContainer var result bytes.Buffer err = tmpl.Execute(&result, templateData) if err != nil { util.LogError(err) } // Create the destination file ioutil.WriteFile(destination, []byte(result.String()), 0644) }
/** * Start a container as binary */ func Run(name, currentPath string, arguments []string, ports, environments map[string]string) { runFunc := reflect.ValueOf(exec.Command) rawArgs := []string{getDockerBinaryPath(), "run", "-v=" + currentPath + ":" + currentPath, "-w=" + currentPath} // Add environments util.Debug(environments) for envName, envValue := range environments { rawArgs = append(rawArgs, "-e="+envName+"="+envValue) } // Add ports for portIn, portOut := range ports { rawArgs = append(rawArgs, "-p="+string(portIn)+":"+string(portOut)) } rawArgs = append(rawArgs, name) // Add user arguments for _, argument := range arguments { rawArgs = append(rawArgs, argument) } runCmd := runFunc.Call(util.BuildReflectArguments(rawArgs))[0].Interface().(*exec.Cmd) runCmd.Stdout = os.Stdout runCmd.Stdin = os.Stdin runCmd.Stderr = os.Stderr util.Debug("Run command:", runCmd.Args) if err := runCmd.Start(); err != nil { util.LogError(err) } }
func Build(name, path string) { buildFunc := reflect.ValueOf(exec.Command) rawArgs := []string{getDockerBinaryPath(), "build"} if *noCache { rawArgs = append(rawArgs, "--no-cache") } rawArgs = append(rawArgs, "-t", name, path) util.Debug(rawArgs) buildCmd := buildFunc.Call(util.BuildReflectArguments(rawArgs))[0].Interface().(*exec.Cmd) buildCmd.Stdin = os.Stdin out, err := buildCmd.CombinedOutput() if err != nil { util.Print(string(out)) util.LogError("Error while starting container '" + name + "'") } buildCmd.Wait() time.Sleep(1 * time.Second) }
func Build(name, path string) { buildFunc := reflect.ValueOf(exec.Command) rawArgs := []string{getDockerBinaryPath(), "build"} if *noCache { rawArgs = append(rawArgs, "--no-cache") } rawArgs = append(rawArgs, "-t", name, path) util.Debug(rawArgs) buildCmd := buildFunc.Call(util.BuildReflectArguments(rawArgs))[0].Interface().(*exec.Cmd) buildCmd.Stderr = os.Stderr if !*quiet { buildCmd.Stdout = os.Stdout } if err := buildCmd.Run(); err != nil { util.LogError(err) } buildCmd.Wait() time.Sleep(1 * time.Second) }
/** * Start a container as a server */ func Start(name, image string, links []string, ports, volumes, environments map[string]string) string { runFunc := reflect.ValueOf(exec.Command) rawArgs := []string{getDockerBinaryPath(), "run", "-d", "-i", "-t", "--privileged", "--name=" + name} // Add environments util.Debug(environments) for envName, envValue := range environments { rawArgs = append(rawArgs, "-e="+envName+"="+envValue) } // Add links for _, link := range links { rawArgs = append(rawArgs, "--link="+link+":"+link) } // Add ports for portIn, portOut := range ports { rawArgs = append(rawArgs, "-p="+string(portIn)+":"+string(portOut)) } // Add volumes for volumeHost, volumeContainer := range volumes { rawArgs = append(rawArgs, "-v="+volumeHost+":"+volumeContainer) } rawArgs = append(rawArgs, image) // Initiate the command with several arguments runCmd := runFunc.Call(util.BuildReflectArguments(rawArgs))[0].Interface().(*exec.Cmd) util.Debug("Start command:", runCmd.Args) out, err := runCmd.CombinedOutput() if err != nil { util.LogError(string(out)) } // Inspect container to check status code exitCodeBuff, _ := Inspect(name, "--format", "{{.State.ExitCode}}") exitCode, _ := strconv.Atoi(strings.TrimSpace(string(exitCodeBuff))) if exitCode != 0 { error, _ := Logs(name) util.LogError("Error while starting container '" + name + "' : " + error) } return string(out) }
func (c *Container) RetrieveIp() { inspect, err := docker.Inspect(c.Id) if err != nil { util.LogError(err) } c.retrieveInfoFromInspection(inspect) }
func (gaudi *Gaudi) Init(content string) { err := goyaml.Unmarshal([]byte(content), &gaudi) if err != nil { util.LogError(err) } emptyCmdForContainers = strings.Split(*emptyCmdFlag, ",") // Init all containers gaudi.Applications.AddAmbassadors() gaudi.All = containerCollection.Merge(gaudi.Applications, gaudi.Binaries) if len(gaudi.All) == 0 { util.LogError("No application or binary to start. Are you missing a 'applications' or 'binaries' field in your configuration ?") } hasGaudiManagedContainer := gaudi.All.Init(gaudi.ApplicationDir) // Apply extends gaudi.applyInheritance() // Check if docker is installed if !docker.HasDocker() { util.LogError("Docker should be installed to use Gaudi (see: https://www.docker.io/gettingstarted/).") } // Check if base image is pulled if hasGaudiManagedContainer && !docker.ImageExists(DEFAULT_BASE_IMAGE) { util.PrintGreen("Pulling base image (this may take a few minutes) ...") docker.Pull(DEFAULT_BASE_IMAGE_WITH_TAG) } if gaudi.useNewVersion() { os.RemoveAll(TEMPLATE_DIR) } // Check if templates are present if !util.IsDir(TEMPLATE_DIR) { util.PrintGreen("Retrieving templates ...") retrieveTemplates() extractTemplates() } gaudi.build() }
func Remove(name string) { removeCmd := exec.Command(getDockerBinaryPath(), "rm", name) removeErr := removeCmd.Start() if removeErr != nil { util.LogError(removeErr) } time.Sleep(1 * time.Second) }
func Kill(name string) { killCommand := exec.Command(getDockerBinaryPath(), "kill", name) killErr := killCommand.Start() if killErr != nil { util.LogError(killErr) } time.Sleep(1 * time.Second) }
func (collection ContainerCollection) Init(relativePath string) bool { hasGaudiManagedContainer := false // Fill name & dependencies for name, currentContainer := range collection { currentContainer.Name = name currentContainer.Init() if currentContainer.IsGaudiManaged() { hasGaudiManagedContainer = true currentContainer.Image = "gaudi/" + name } for _, dependency := range currentContainer.Links { if depContainer, exists := collection[dependency]; exists { currentContainer.AddDependency(depContainer) } else { util.LogError(name + " references a non existing application : " + dependency) } } // Add relative path to volumes for volumeHost, volumeContainer := range currentContainer.Volumes { // Relative volume host if string(volumeHost[0]) != "/" { delete(currentContainer.Volumes, volumeHost) volumeHost = relativePath + "/" + volumeHost currentContainer.Volumes[volumeHost] = volumeContainer } // Create directory if needed if !util.IsDir(volumeHost) { err := os.MkdirAll(volumeHost, 0755) if err != nil { util.LogError(err) } } } } return hasGaudiManagedContainer }
func retrieveConfigPath(configFile string) string { if len(configFile) == 0 { util.LogError("Config file name cannot be empty.") } if string(configFile[0]) != "/" { currentDir, err := os.Getwd() if err != nil { util.LogError(err) } configFile = currentDir + "/" + configFile } if !util.IsFile(configFile) { util.LogError("Configuration file not found: '" + configFile + "'. Use --config to specify a custom file") } return configFile }
func (gaudi *Gaudi) InitFromFile(file string) { gaudi.ConfigurationPath = file gaudi.ApplicationDir = path.Dir(file) fileContent, err := ioutil.ReadFile(file) if err != nil { util.LogError(err) } gaudi.Init(string(fileContent)) }
func retrieveTemplates() { os.MkdirAll(TEMPLATE_DIR, 0755) archive, err := os.Create(TEMPLATE_DIR + "templates.tar") if err != nil { util.LogError(err) } defer archive.Close() content, err := http.Get(TEMPLATE_REMOTE_PATH) if err != nil { util.LogError(err) } defer content.Body.Close() _, err = io.Copy(archive, content.Body) if err != nil { util.LogError(err) } }
func extractTemplates() { tarFile, _ := os.Open(TEMPLATE_DIR + "templates.tar") defer tarFile.Close() tar := tar.NewReader(tarFile) for { header, err := tar.Next() if err == io.EOF { break } if err != nil { util.LogError(err) } // Remove first path part filePath := strings.Join(strings.Split(header.Name, "/")[1:], "/") // Check if we should create a folder or a file if header.Size == 0 { err := os.MkdirAll(TEMPLATE_DIR+filePath, 0755) if err != nil { util.LogError(err) } } else { f, err := os.Create(TEMPLATE_DIR + filePath) if err != nil { util.LogError(err) } defer f.Close() _, err = io.Copy(f, tar) if err != nil { util.LogError(err) } } } os.Remove(TEMPLATE_DIR + "templates.tar") }
func Pull(name string) { pullCmd := exec.Command(getDockerBinaryPath(), "pull", name) pullCmd.Stderr = os.Stderr pullCmd.Stdin = os.Stdin util.Debug("Pull command:", pullCmd.Args) if err := pullCmd.Run(); err != nil { util.LogError(err) } pullCmd.Wait() }
func startOne(currentContainer *container.Container, rebuild bool, done map[string]chan bool) { // Waiting for dependencies to be started for _, dependency := range currentContainer.Dependencies { if dependency.Name == currentContainer.Name { util.LogError("Application " + currentContainer.Name + " can't be linked with itself.") } <-done[dependency.Name] } currentContainer.Start(rebuild) close(done[currentContainer.Name]) }
func Enter(name string) { var pid string var imageExists bool nsenter, _ := exec.LookPath("nsenter") ps, _ := SnapshotProcesses() if pid, imageExists = ps[name]; !imageExists { util.LogError("Image " + name + " doesn't exists") } statePidBuff, _ := Inspect(pid, "--format", "{{.State.Pid}}") statePid := strings.TrimSpace(string(statePidBuff)) enterCmd := exec.Command("sudo", nsenter, "--target", statePid, "--mount", "--uts", "--ipc", "--net", "--pid") enterCmd.Stdout = os.Stdout enterCmd.Stdin = os.Stdin enterCmd.Stderr = os.Stderr util.Debug("Run command:", enterCmd.Args) if err := enterCmd.Run(); err != nil { util.LogError(err) } }
/** * Enter in a specific container */ func (gaudi *Gaudi) Enter(name string) { // Check if nsenter exists images, err := docker.GetImages() if err != nil { util.LogError(err) } if _, ok := images["jpetazzo/nsenter"]; !ok { // Pull ns-enter image util.PrintGreen("Retrieving ns-enter image ...") docker.Exec([]string{"run", "--rm", "-v", "/usr/local/bin:/target", "jpetazzo/nsenter"}, false) } container := gaudi.All[name] docker.Enter(container.GetFullName()) }
func Exec(args []string, hasStdout bool) { execFunc := reflect.ValueOf(exec.Command) execCmd := execFunc.Call(util.BuildReflectArguments(args))[0].Interface().(*exec.Cmd) if hasStdout { execCmd.Stdout = os.Stdout } execCmd.Stdin = os.Stdin execCmd.Stderr = os.Stderr util.Debug("Exec command:", execCmd.Args) if err := execCmd.Start(); err != nil { util.LogError(err) } }
/** * Check if all applications are started */ func (gaudi *Gaudi) Check() { images, err := docker.SnapshotProcesses() if err != nil { util.LogError(err) } for _, currentContainer := range gaudi.Applications { if containerId, ok := images[currentContainer.Image]; ok { currentContainer.Id = containerId currentContainer.RetrieveIp() util.PrintOrange("Application", currentContainer.Name, "is running", "("+currentContainer.Ip+":"+currentContainer.GetFirstPort()+")") } else { util.PrintOrange("Application", currentContainer.Name, "is not running") } } }
func getIncludes() map[string]string { includesDir := TEMPLATE_DIR + "_includes/" result := make(map[string]string) files, err := ioutil.ReadDir(includesDir) if err != nil { util.LogError(err) } for _, file := range files { name := strings.Split(file.Name(), ".")[0] content, _ := ioutil.ReadFile(includesDir + file.Name()) result[name] = string(content) } return result }
func main() { flag.Parse() if *flagVersion { fmt.Println(gaudi.VERSION) return } rebuild := len(flag.Args()) > 0 && flag.Args()[0] == "rebuild" g := gaudi.Gaudi{} g.InitFromFile(retrieveConfigPath(*config)) if len(flag.Args()) == 0 || rebuild { // Start all applications g.StartApplications(rebuild) } else { switch os.Args[1] { case "run": // Run a specific command g.Run(os.Args[2], os.Args[3:]) break case "enter": // Enter in a specific container g.Enter(os.Args[2]) break case "stop": // Stop all applications g.StopApplications() break case "check": // Check if all applications are running g.Check() break case "clean": // Clean application containers g.Clean() break default: util.LogError("Argument " + os.Args[1] + " was not found") break } } }
/** * Start a container as a server */ func Start(name, image string, links []string, ports, volumes, environments map[string]string) string { runFunc := reflect.ValueOf(exec.Command) rawArgs := []string{getDockerBinaryPath(), "run", "-d", "-i", "-t", "--privileged", "--name=" + name} // Add environments util.Debug(environments) for envName, envValue := range environments { rawArgs = append(rawArgs, "-e="+envName+"="+envValue) } // Add links for _, link := range links { rawArgs = append(rawArgs, "--link="+link+":"+link) } // Add ports for portIn, portOut := range ports { rawArgs = append(rawArgs, "-p="+string(portIn)+":"+string(portOut)) } // Add volumes for volumeHost, volumeContainer := range volumes { rawArgs = append(rawArgs, "-v="+volumeHost+":"+volumeContainer) } rawArgs = append(rawArgs, image) // Initiate the command with several arguments runCmd := runFunc.Call(util.BuildReflectArguments(rawArgs))[0].Interface().(*exec.Cmd) util.Debug("Start command:", runCmd.Args) out, err := runCmd.CombinedOutput() if err != nil { util.LogError(string(out)) } return string(out) }
func (gaudi *Gaudi) applyInheritance() { extendsChan := make(map[string]chan bool) for name, currentContainer := range gaudi.Applications { extendsChan[name] = make(chan bool) if currentContainer.Extends == "" { close(extendsChan[name]) continue } if parentContainer, exists := gaudi.All[currentContainer.Extends]; exists { go extendsOne(currentContainer, parentContainer, extendsChan) } else { util.LogError(currentContainer.Name + " extends a non existing application : " + currentContainer.Extends) } } // Waiting for all applications to be extended for name, _ := range extendsChan { <-extendsChan[name] } }
func (collection ContainerCollection) CheckIfNotEmpty() { // Check if there is at least a container if collection == nil || len(collection) == 0 { util.LogError("Gaudi requires at least an application to be defined to start anything") } }
func (gaudi *Gaudi) build() { // Retrieve application Path currentDirctory, _ := os.Getwd() err := os.MkdirAll(PARSED_TEMPLATE_DIR, 0700) if err != nil { util.LogError(err) } // Retrieve includes includes := getIncludes() for _, currentContainer := range gaudi.All { // Check if the container has a type if currentContainer.Type == "" { util.LogError("Container " + currentContainer.Name + " should have a field called 'type'.") } if !currentContainer.IsGaudiManaged() { continue } // Check if the beforeScript is a file beforeScript := currentContainer.BeforeScript if len(beforeScript) != 0 { copied := gaudi.copyRelativeFiles(beforeScript, PARSED_TEMPLATE_DIR+currentContainer.Name+"/") if copied { currentContainer.BeforeScript = "./" + currentContainer.BeforeScript } } // Check if the afterScript is a file afterScript := currentContainer.AfterScript if len(afterScript) != 0 { copied := gaudi.copyRelativeFiles(afterScript, PARSED_TEMPLATE_DIR+currentContainer.Name+"/") if copied { currentContainer.AfterScript = "./" + currentContainer.AfterScript } } templateDir, isCustom := gaudi.GetContainerTemplate(currentContainer) files, err := ioutil.ReadDir(templateDir) if err != nil { util.LogError("Application '" + currentContainer.Type + "' is not supported. Check http://gaudi.io/components.html for a list of supported applications.") } err = os.MkdirAll(PARSED_TEMPLATE_DIR+currentContainer.Name, 0755) if err != nil { util.LogError(err) } sourceTemplateDir := TEMPLATE_DIR + currentContainer.Type if isCustom { sourceTemplateDir = templateDir } // Parse & copy files for _, file := range files { gaudi.parseTemplate(sourceTemplateDir, PARSED_TEMPLATE_DIR, file, includes, currentContainer) } // Copy all files marked as Add for fileToAdd := range currentContainer.Add { filePath := currentDirctory + "/" + fileToAdd directories := strings.Split(fileToAdd, "/") if len(directories) > 1 { os.MkdirAll(PARSED_TEMPLATE_DIR+currentContainer.Name+"/"+strings.Join(directories[0:len(directories)-1], "/"), 0755) } err := util.Copy(PARSED_TEMPLATE_DIR+currentContainer.Name+"/"+fileToAdd, filePath) if err != nil { util.LogError(err) } } } }