func (j roleBuildJob) Run() { select { case <-j.abort: j.resultsCh <- nil return default: } roleImageName := GetRoleDevImageName(j.repository, j.role, j.role.GetRoleDevVersion()) if !j.force { if hasImage, err := j.dockerManager.HasImage(roleImageName); err != nil { j.resultsCh <- err return } else if hasImage { j.ui.Printf("Skipping build of role image %s because it exists\n", color.YellowString(j.role.Name)) j.resultsCh <- nil return } } if j.builder.metricsPath != "" { seriesName := fmt.Sprintf("create-role-images::%s", roleImageName) stampy.Stamp(j.builder.metricsPath, "fissile", seriesName, "start") defer stampy.Stamp(j.builder.metricsPath, "fissile", seriesName, "done") } j.ui.Printf("Creating Dockerfile for role %s ...\n", color.YellowString(j.role.Name)) dockerfileDir, err := j.builder.CreateDockerfileDir(j.role, j.baseImageName) if err != nil { j.resultsCh <- fmt.Errorf("Error creating Dockerfile and/or assets for role %s: %s", j.role.Name, err.Error()) return } if j.noBuild { j.ui.Printf("Skipping build of role image %s because of flag\n", color.YellowString(j.role.Name)) j.resultsCh <- nil return } if !strings.HasSuffix(dockerfileDir, string(os.PathSeparator)) { dockerfileDir = fmt.Sprintf("%s%c", dockerfileDir, os.PathSeparator) } j.ui.Printf("Building docker image of %s in %s ...\n", color.YellowString(j.role.Name), color.YellowString(dockerfileDir)) log := new(bytes.Buffer) stdoutWriter := docker.NewFormattingWriter( log, docker.ColoredBuildStringFunc(roleImageName), ) err = j.dockerManager.BuildImage(dockerfileDir, roleImageName, stdoutWriter) if err != nil { log.WriteTo(j.ui) j.resultsCh <- fmt.Errorf("Error building image: %s", err.Error()) return } j.resultsCh <- nil }
// GenerateBaseDockerImage generates a base docker image to be used as a FROM for role images func (f *Fissile) GenerateBaseDockerImage(targetPath, baseImage, metricsPath string, noBuild bool, repository string) error { if metricsPath != "" { stampy.Stamp(metricsPath, "fissile", "create-role-base", "start") defer stampy.Stamp(metricsPath, "fissile", "create-role-base", "done") } dockerManager, err := docker.NewImageManager() if err != nil { return fmt.Errorf("Error connecting to docker: %s", err.Error()) } baseImageName := builder.GetBaseImageName(repository, f.Version) image, err := dockerManager.FindImage(baseImageName) if err == docker.ErrImageNotFound { f.UI.Println("Image doesn't exist, it will be created ...") } else if err != nil { return fmt.Errorf("Error looking up image: %s", err.Error()) } else { f.UI.Println(color.GreenString( "Base role image %s with ID %s already exists. Doing nothing.", color.YellowString(baseImageName), color.YellowString(image.ID), )) return nil } if !strings.HasSuffix(targetPath, string(os.PathSeparator)) { targetPath = fmt.Sprintf("%s%c", targetPath, os.PathSeparator) } baseImageBuilder := builder.NewBaseImageBuilder(baseImage) if noBuild { f.UI.Println("Skipping image build because of flag.") return nil } f.UI.Println("Building base docker image ...") log := new(bytes.Buffer) stdoutWriter := docker.NewFormattingWriter( log, docker.ColoredBuildStringFunc(baseImageName), ) tarPopulator := baseImageBuilder.NewDockerPopulator() err = dockerManager.BuildImageFromCallback(baseImageName, stdoutWriter, tarPopulator) if err != nil { log.WriteTo(f.UI) return fmt.Errorf("Error building base image: %s", err) } f.UI.Println(color.GreenString("Done.")) return nil }
// GeneratePackagesRoleImage builds the docker image for the packages layer // where all packages are included func (f *Fissile) GeneratePackagesRoleImage(repository string, roleManifest *model.RoleManifest, noBuild, force bool, packagesImageBuilder *builder.PackagesImageBuilder) error { if len(f.releases) == 0 { return fmt.Errorf("Releases not loaded") } dockerManager, err := docker.NewImageManager() if err != nil { return fmt.Errorf("Error connecting to docker: %s", err.Error()) } packagesLayerImageName := packagesImageBuilder.GetRolePackageImageName(roleManifest) if !force { if hasImage, err := dockerManager.HasImage(packagesLayerImageName); err == nil && hasImage { f.UI.Printf("Packages layer %s already exists. Skipping ...\n", color.YellowString(packagesLayerImageName)) return nil } } baseImageName := builder.GetBaseImageName(repository, f.Version) if hasImage, err := dockerManager.HasImage(baseImageName); err != nil { return fmt.Errorf("Error getting base image: %s", err) } else if !hasImage { return fmt.Errorf("Failed to find role base %s, did you build it first?", baseImageName) } if noBuild { f.UI.Println("Skipping packages layer docker image build because of --no-build flag.") return nil } f.UI.Printf("Building packages layer docker image %s ...\n", color.YellowString(packagesLayerImageName)) log := new(bytes.Buffer) stdoutWriter := docker.NewFormattingWriter( log, docker.ColoredBuildStringFunc(packagesLayerImageName), ) tarPopulator := packagesImageBuilder.NewDockerPopulator(roleManifest, force) err = dockerManager.BuildImageFromCallback(packagesLayerImageName, stdoutWriter, tarPopulator) if err != nil { log.WriteTo(f.UI) return fmt.Errorf("Error building packages layer docker image: %s", err.Error()) } f.UI.Println(color.GreenString("Done.")) return nil }
func (c *Compilator) compilePackage(pkg *model.Package) (err error) { // Prepare input dir (package plus deps) if err := c.createCompilationDirStructure(pkg); err != nil { return err } if err := c.copyDependencies(pkg); err != nil { return err } // Generate a compilation script targetScriptName := "compile.sh" hostScriptPath := filepath.Join(pkg.GetTargetPackageSourcesDir(c.hostWorkDir), targetScriptName) containerScriptPath := filepath.Join(docker.ContainerInPath, targetScriptName) if err := compilation.SaveScript(c.baseType, compilation.CompilationScript, hostScriptPath); err != nil { return err } // Extract package extractDir := c.getSourcePackageDir(pkg) if _, err := pkg.Extract(extractDir); err != nil { return err } // Run compilation in container containerName := c.getPackageContainerName(pkg) // in-memory buffer of the log log := new(bytes.Buffer) stdoutWriter := docker.NewFormattingWriter( log, func(line string) string { return color.GreenString("compilation-%s > %s", color.MagentaString("%s", pkg.Name), color.WhiteString("%s", line)) }, ) stderrWriter := docker.NewFormattingWriter( log, func(line string) string { return color.GreenString("compilation-%s > %s", color.MagentaString("%s", pkg.Name), color.RedString("%s", line)) }, ) sourceMountName := fmt.Sprintf("source_mount-%s", uuid.New()) mounts := map[string]string{ pkg.GetTargetPackageSourcesDir(c.hostWorkDir): docker.ContainerInPath, pkg.GetPackageCompiledTempDir(c.hostWorkDir): docker.ContainerOutPath, // Add the volume mount to work around AUFS issues. We will clean // the volume up (as long as we're not trying to keep the container // around for debugging). We don't give it an actual directory to mount // from, so it will be in some docker-maintained storage. sourceMountName: ContainerSourceDir, } exitCode, container, err := c.dockerManager.RunInContainer(docker.RunInContainerOpts{ ContainerName: containerName, ImageName: c.BaseImageName(), Cmd: []string{"bash", containerScriptPath, pkg.Name, pkg.Version}, Mounts: mounts, Volumes: map[string]map[string]string{sourceMountName: nil}, KeepContainer: c.keepContainer, StdoutWriter: stdoutWriter, StderrWriter: stderrWriter, }) if container != nil && (!c.keepContainer || err == nil || exitCode == 0) { // Attention. While the assignments to 'err' in the // deferal below take effect after the 'return' // statements coming later they are visible to the // caller, i.e. override the 'return'ed value, // because 'err' is a __named__ return parameter. defer func() { // Remove container - dockerManager.RemoveContainer does a force-rm if removeErr := c.dockerManager.RemoveContainer(container.ID); removeErr != nil { if err == nil { err = removeErr } else { err = fmt.Errorf("Error compiling package: %s. Error removing package: %s", err.Error(), removeErr.Error()) } } if removeErr := c.dockerManager.RemoveVolumes(container); removeErr != nil { if err == nil { err = removeErr } else { err = fmt.Errorf("%s: Error removing volumes for package %s: %s", err, pkg.Name, removeErr) } } }() } if err != nil { log.WriteTo(c.ui) return fmt.Errorf("Error compiling package %s: %s", pkg.Name, err.Error()) } if exitCode != 0 { log.WriteTo(c.ui) return fmt.Errorf("Error - compilation for package %s exited with code %d", pkg.Name, exitCode) } return os.Rename( pkg.GetPackageCompiledTempDir(c.hostWorkDir), pkg.GetPackageCompiledDir(c.hostWorkDir)) }
// CreateCompilationBase will create the compiler container func (c *Compilator) CreateCompilationBase(baseImageName string) (image *dockerClient.Image, err error) { imageTag := c.baseCompilationImageTag() imageName := c.BaseImageName() c.ui.Println(color.GreenString("Using %s as a compilation image name", color.YellowString(imageName))) containerName := c.baseCompilationContainerName() c.ui.Println(color.GreenString("Using %s as a compilation container name", color.YellowString(containerName))) image, err = c.dockerManager.FindImage(imageName) if err != nil { c.ui.Println("Image doesn't exist, it will be created ...") } else { c.ui.Println(color.GreenString( "Compilation image %s with ID %s already exists. Doing nothing.", color.YellowString(imageName), color.YellowString(image.ID), )) return image, nil } tempScriptDir, err := util.TempDir("", "fissile-compilation") if err != nil { return nil, fmt.Errorf("Could not create temp dir %s: %s", tempScriptDir, err.Error()) } defer os.RemoveAll(tempScriptDir) targetScriptName := "compilation-prerequisites.sh" containerScriptPath := filepath.Join(docker.ContainerInPath, targetScriptName) hostScriptPath := filepath.Join(tempScriptDir, targetScriptName) if err = compilation.SaveScript(c.baseType, compilation.PrerequisitesScript, hostScriptPath); err != nil { return nil, fmt.Errorf("Error saving script asset: %s", err.Error()) } // in-memory buffer of the log log := new(bytes.Buffer) stdoutWriter := docker.NewFormattingWriter( log, func(line string) string { return color.GreenString("compilation-container > %s", color.WhiteString("%s", line)) }, ) stderrWriter := docker.NewFormattingWriter( log, func(line string) string { return color.GreenString("compilation-container > %s", color.RedString("%s", line)) }, ) exitCode, container, err := c.dockerManager.RunInContainer(docker.RunInContainerOpts{ ContainerName: containerName, ImageName: baseImageName, Cmd: []string{"bash", "-c", containerScriptPath}, Mounts: map[string]string{tempScriptDir: docker.ContainerInPath}, KeepContainer: false, // There is never a need to keep this container on failure StdoutWriter: stdoutWriter, StderrWriter: stderrWriter, }) if container != nil { defer func() { removeErr := c.dockerManager.RemoveContainer(container.ID) if removeErr != nil { if err == nil { err = removeErr } else { err = fmt.Errorf( "Image creation error: %s. Image removal error: %s", err, removeErr, ) } } }() } if err != nil { log.WriteTo(c.ui) return nil, fmt.Errorf("Error running script: %s", err.Error()) } if exitCode != 0 { log.WriteTo(c.ui) return nil, fmt.Errorf("Error - script script exited with code %d", exitCode) } image, err = c.dockerManager.CreateImage( container.ID, c.baseCompilationImageRepository(), imageTag, "", []string{}, ) if err != nil { return nil, fmt.Errorf("Error creating image %s", err.Error()) } c.ui.Println(color.GreenString( "Image %s with ID %s created successfully.", color.YellowString(c.BaseImageName()), color.YellowString(image.ID))) return image, nil }