Exemple #1
0
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
}
Exemple #2
0
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)
}
Exemple #3
0
// 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
}
Exemple #4
0
// 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)
}
Exemple #5
0
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
}
Exemple #6
0
func renderACI(acipath, scratchpath, depstore string, insecure bool) ([]string, error) {
	reg := registry.Registry{
		Depstore:    depstore,
		Scratchpath: scratchpath,
		Insecure:    insecure,
	}

	man, err := util.GetManifest(acipath)
	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(scratchpath, depkey), reg)
		if err != nil {
			return nil, err
		}
		deplist = append(deplist, subdeplist...)
	}

	return deplist, nil
}
Exemple #7
0
// 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, a.OverlayWorkPath is the work directory used by
// overlayfs, and insecure signifies whether downloaded images should be
// fetched over http or https.
func (a *ACBuild) Run(cmd []string, insecure bool) (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")
	}

	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 {
				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 nspawnpath string
	if deps == nil {
		nspawnpath = 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
			}
		}()

		nspawnpath = a.OverlayTargetPath
	}
	nspawncmd := []string{"systemd-nspawn", "-q", "-D", nspawnpath}

	if man.App != nil {
		for _, evar := range man.App.Environment {
			nspawncmd = append(nspawncmd, "--setenv", evar.Name+"="+evar.Value)
		}
	}

	err = a.mirrorLocalZoneInfo()
	if err != nil {
		return err
	}

	if len(cmd) == 0 {
		return fmt.Errorf("command to run not set")
	}
	abscmd, err := findCmdInPath(pathlist, cmd[0], nspawnpath)
	if err != nil {
		return err
	}
	nspawncmd = append(nspawncmd, abscmd)
	nspawncmd = append(nspawncmd, cmd[1:]...)

	execCmd := exec.Command(nspawncmd[0], nspawncmd[1:]...)
	execCmd.Stdin = os.Stdin
	execCmd.Stdout = os.Stdout
	execCmd.Stderr = os.Stderr
	execCmd.Env = []string{"SYSTEMD_LOG_LEVEL=err"}

	err = execCmd.Run()
	if err != nil {
		if err == exec.ErrNotFound {
			return fmt.Errorf("systemd-nspawn is required but not found")
		}
		return err
	}

	return nil
}
Exemple #8
0
// 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
}
Exemple #9
0
// Run will execute the given command in the ACI being built. acipath is where
// the untarred ACI is stored, depstore is the directory to download
// dependencies into, scratchpath is where the dependencies are expanded into,
// workpath is the work directory used by overlayfs, and insecure signifies
// whether downloaded images should be fetched over http or https.
func Run(acipath, depstore, targetpath, scratchpath, workpath string, cmd []string, insecure bool) error {
	err := util.RmAndMkdir(targetpath)
	if err != nil {
		return err
	}
	defer os.RemoveAll(targetpath)
	err = util.RmAndMkdir(workpath)
	if err != nil {
		return err
	}
	defer os.RemoveAll(workpath)
	err = os.MkdirAll(scratchpath, 0755)
	if err != nil {
		return err
	}
	err = os.MkdirAll(depstore, 0755)
	if err != nil {
		return err
	}

	man, err := util.GetManifest(acipath)
	if err != nil {
		return err
	}

	if len(man.Dependencies) != 0 {
		err := util.Exec("modprobe", "overlay")
		if err != nil {
			return err
		}
		if !supportsOverlay() {
			return fmt.Errorf(
				"overlayfs support required for using run with dependencies")
		}
	}

	deps, err := renderACI(acipath, scratchpath, depstore, insecure)
	if err != nil {
		return err
	}

	var nspawnpath string
	if deps == nil {
		nspawnpath = path.Join(acipath, aci.RootfsDir)
	} else {
		for i, dep := range deps {
			deps[i] = path.Join(scratchpath, dep, aci.RootfsDir)
		}
		options := "-olowerdir=" + strings.Join(deps, ":") +
			",upperdir=" + path.Join(acipath, aci.RootfsDir) + ",workdir=" + workpath
		err := util.Exec("mount", "-t", "overlay",
			"overlay", options, targetpath)
		if err != nil {
			return err
		}

		umount := exec.Command("umount", targetpath)
		umount.Stdout = os.Stdout
		umount.Stderr = os.Stderr
		defer umount.Run()

		nspawnpath = targetpath
	}
	nspawncmd := []string{"systemd-nspawn", "-q", "-D", nspawnpath}

	if man.App != nil {
		for _, evar := range man.App.Environment {
			nspawncmd = append(nspawncmd, "--setenv", evar.Name+"="+evar.Value)
		}
	}

	if len(cmd) == 0 {
		return fmt.Errorf("command to run not set")
	}
	abscmd, err := findCmdInPath(pathlist, cmd[0], nspawnpath)
	if err != nil {
		return err
	}
	nspawncmd = append(nspawncmd, abscmd)
	nspawncmd = append(nspawncmd, cmd[1:]...)
	//fmt.Printf("%v\n", nspawncmd)

	err = util.Exec(nspawncmd[0], nspawncmd[1:]...)
	if err != nil {
		return err
	}

	return nil
}
Exemple #10
0
// 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))
}
Exemple #11
0
// 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

	ex, err := util.Exists(output)
	if err != nil {
		return err
	}
	if ex {
		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()

	// 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 {
		return err
	}

	if sign {
		err = signACI(output, output+".asc", gpgflags)
		if err != nil {
			os.Remove(output)
			os.Remove(output + ".asc")
			return err
		}
	}

	return nil
}
Exemple #12
0
// 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.Scratchpath, key))
}