func addACBuildAnnotation(cmd *cobra.Command, args []string) error { const annoNamePattern = "appc.io/acbuild/command-%d" acb := newACBuild() man, err := util.GetManifest(acb.CurrentACIPath) if err != nil { return err } var acbuildCount int for _, ann := range man.Annotations { var tmpCount int n, _ := fmt.Sscanf(string(ann.Name), annoNamePattern, &tmpCount) if n == 1 && tmpCount > acbuildCount { acbuildCount = tmpCount } } command := cmd.Name() tmpcmd := cmd.Parent() for { command = tmpcmd.Name() + " " + command if tmpcmd == cmdAcbuild { break } tmpcmd = tmpcmd.Parent() } for _, a := range args { command += fmt.Sprintf(" %q", a) } return acb.AddAnnotation(fmt.Sprintf(annoNamePattern, acbuildCount+1), command) }
func genDeplist(acipath string, reg registry.Registry) ([]string, error) { man, err := util.GetManifest(acipath) if err != nil { return nil, err } key, err := reg.GetACI(man.Name, man.Labels) if err != nil { fmt.Printf("Name: %s", man.Name) return nil, err } var deps []string for _, dep := range man.Dependencies { depkey, err := reg.GetACI(dep.ImageName, dep.Labels) if err != nil { return nil, err } subdeps, err := genDeplist(path.Join(reg.DepStoreExpandedPath, depkey), reg) if err != nil { return nil, err } deps = append(deps, subdeps...) } deps = append(deps, key) return deps, nil }
// Returns the key for the ACI with the given name and labels func (r Registry) GetACI(name types.ACIdentifier, labels types.Labels) (string, error) { files, err := ioutil.ReadDir(r.DepStoreExpandedPath) if err != nil { return "", err } nextkey: for _, file := range files { man, err := util.GetManifest(path.Join(r.DepStoreExpandedPath, file.Name())) if err != nil { return "", err } if man.Name != name { continue } for _, l := range labels { val, ok := man.Labels.Get(l.Name.String()) if !ok { continue } if val != l.Value { continue nextkey } } return file.Name(), nil } return "", ErrNotFound }
// CatManifest will print to stdout the manifest from the expanded ACI stored // at a.CurrentACIPath, optionally inserting whitespace to make it more human // readable. func (a *ACBuild) CatManifest(prettyPrint bool) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() man, err := util.GetManifest(a.CurrentACIPath) if err != nil { return err } return util.PrintManifest(man, prettyPrint) }
func (a *ACBuild) renderACI(insecure, debug bool) ([]string, error) { reg := registry.Registry{ DepStoreTarPath: a.DepStoreTarPath, DepStoreExpandedPath: a.DepStoreExpandedPath, Insecure: insecure, Debug: debug, } man, err := util.GetManifest(a.CurrentACIPath) if err != nil { return nil, err } if len(man.Dependencies) == 0 { return nil, nil } var deplist []string for _, dep := range man.Dependencies { err := reg.FetchAndRender(dep.ImageName, dep.Labels, dep.Size) if err != nil { return nil, err } depkey, err := reg.GetACI(dep.ImageName, dep.Labels) if err != nil { return nil, err } subdeplist, err := genDeplist(path.Join(a.DepStoreExpandedPath, depkey), reg) if err != nil { return nil, err } deplist = append(deplist, subdeplist...) } return deplist, nil }
// Run will execute the given command in the ACI being built. a.CurrentACIPath // is where the untarred ACI is stored, a.DepStoreTarPath is the directory to // download dependencies into, a.DepStoreExpandedPath is where the dependencies // are expanded into, and a.OverlayWorkPath is the work directory used by // overlayfs. // // Arguments: // // - cmd: The command to run and its arguments. // // - workingDir: If specified, the current directory inside the container is // changed to its value before running the given command. // // - runEngine: The engine used to perform the execution of the command. func (a *ACBuild) Run(cmd []string, workingDir string, insecure bool, runEngine engine.Engine) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() if os.Geteuid() != 0 { return fmt.Errorf("the run subcommand must be run as root") } if len(cmd) == 0 { return fmt.Errorf("command to run not set") } err = util.MaybeUnmount(a.OverlayTargetPath) if err != nil { return err } err = util.RmAndMkdir(a.OverlayTargetPath) if err != nil { return err } defer os.RemoveAll(a.OverlayTargetPath) err = util.RmAndMkdir(a.OverlayWorkPath) if err != nil { return err } defer os.RemoveAll(a.OverlayWorkPath) err = os.MkdirAll(a.DepStoreExpandedPath, 0755) if err != nil { return err } err = os.MkdirAll(a.DepStoreTarPath, 0755) if err != nil { return err } man, err := util.GetManifest(a.CurrentACIPath) if err != nil { return err } if len(man.Dependencies) != 0 { if !supportsOverlay() { err := exec.Command("modprobe", "overlay").Run() if err != nil { if _, ok := err.(*exec.ExitError); ok { return fmt.Errorf("overlayfs is not supported on your system") } return err } if !supportsOverlay() { return fmt.Errorf( "overlayfs support required for using run with dependencies") } } } deps, err := a.renderACI(insecure, a.Debug) if err != nil { return err } var chrootDir string if deps == nil { chrootDir = path.Join(a.CurrentACIPath, aci.RootfsDir) } else { for i, dep := range deps { deps[i] = path.Join(a.DepStoreExpandedPath, dep, aci.RootfsDir) } options := "lowerdir=" + strings.Join(deps, ":") + ",upperdir=" + path.Join(a.CurrentACIPath, aci.RootfsDir) + ",workdir=" + a.OverlayWorkPath err := syscall.Mount("overlay", a.OverlayTargetPath, "overlay", 0, options) if err != nil { return err } defer func() { err1 := syscall.Unmount(a.OverlayTargetPath, 0) if err == nil { err = err1 } }() chrootDir = a.OverlayTargetPath } var env types.Environment if man.App != nil { env = man.App.Environment } else { env = types.Environment{} } err = a.mirrorLocalZoneInfo() if err != nil { return err } err = runEngine.Run(cmd[0], cmd[1:], env, chrootDir, workingDir) if err != nil { return err } return nil }
// Write will produce the resulting ACI from the current build context, saving // it to the given path, optionally signing it. func (a *ACBuild) Write(output string, overwrite, sign bool, gpgflags []string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() man, err := util.GetManifest(a.CurrentACIPath) if err != nil { return err } if man.App != nil && len(man.App.Exec) == 0 { fmt.Fprintf(os.Stderr, "warning: exec command was never set.\n") } if man.Name == types.ACIdentifier(placeholdername) { return fmt.Errorf("can't write ACI, name was never set") } fileFlags := os.O_CREATE | os.O_WRONLY _, err = os.Stat(output) switch { case os.IsNotExist(err): break case err != nil: return err default: if !overwrite { return fmt.Errorf("ACI already exists: %s", output) } fileFlags |= os.O_TRUNC } // open/create the aci file ofile, err := os.OpenFile(output, fileFlags, 0644) if err != nil { return err } defer ofile.Close() defer func() { // When write is done, if an error is encountered remove the partial // ACI that had been written. if err != nil { os.Remove(output) os.Remove(output + ".asc") } }() // setup compression gzwriter := gzip.NewWriter(ofile) defer gzwriter.Close() // create the aci writer aw := aci.NewImageWriter(*man, tar.NewWriter(gzwriter)) err = filepath.Walk(a.CurrentACIPath, aci.BuildWalker(a.CurrentACIPath, aw, nil)) defer aw.Close() if err != nil { pathErr, ok := err.(*os.PathError) if !ok { fmt.Printf("not a path error!\n") return err } syscallErrno, ok := pathErr.Err.(syscall.Errno) if !ok { fmt.Printf("not a syscall errno!\n") return err } if pathErr.Op == "open" && syscallErrno != syscall.EACCES { return err } problemPath := pathErr.Path[len(path.Join(a.CurrentACIPath, aci.RootfsDir)):] return fmt.Errorf("%q: permission denied - call write as root", problemPath) } if sign { err = signACI(output, output+".asc", gpgflags) if err != nil { return err } } return nil }
// Returns the manifest for the ACI with the given key func (r Registry) GetImageManifest(key string) (*schema.ImageManifest, error) { return util.GetManifest(path.Join(r.DepStoreExpandedPath, key)) }