Beispiel #1
0
func validate(imOK bool, im io.Reader, rfsOK bool, files []string) error {
	defer func() {
		if rc, ok := im.(io.Closer); ok {
			rc.Close()
		}
	}()
	if !imOK {
		return ErrNoManifest
	}
	if !rfsOK {
		return ErrNoRootFS
	}
	b, err := ioutil.ReadAll(im)
	if err != nil {
		return fmt.Errorf("error reading image manifest: %v", err)
	}
	var a schema.ImageManifest
	if err := a.UnmarshalJSON(b); err != nil {
		return fmt.Errorf("image manifest validation failed: %v", err)
	}
	if a.ACVersion.LessThanMajor(schema.AppContainerVersion) {
		return ErrOldVersion{
			version: a.ACVersion,
		}
	}
	for _, f := range files {
		if !strings.HasPrefix(f, "rootfs") {
			return fmt.Errorf("unrecognized file path in layout: %q", f)
		}
	}
	return nil
}
Beispiel #2
0
// ManifestFromImage extracts a new schema.ImageManifest from the given ACI image.
func ManifestFromImage(rs io.ReadSeeker) (*schema.ImageManifest, error) {
	var im schema.ImageManifest

	tr, err := NewCompressedTarReader(rs)
	if err != nil {
		return nil, err
	}
	defer tr.Close()

	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			return nil, errors.New("missing manifest")
		case nil:
			if filepath.Clean(hdr.Name) == ManifestFile {
				data, err := ioutil.ReadAll(tr)
				if err != nil {
					return nil, err
				}
				if err := im.UnmarshalJSON(data); err != nil {
					return nil, err
				}
				return &im, nil
			}
		default:
			return nil, fmt.Errorf("error extracting tarball: %v", err)
		}
	}
}
Beispiel #3
0
func createImageManifest(imj string) (*schema.ImageManifest, error) {
	var im schema.ImageManifest
	err := im.UnmarshalJSON([]byte(imj))
	if err != nil {
		return nil, err
	}
	return &im, nil
}
Beispiel #4
0
func loadAndValidateACI(path, server string) (data []byte, labels map[string]string, err error) {
	rc, err := openFileMaybeGzipped(path)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to open %q: %v", path, err)
	}
	defer rc.Close()
	tr := tar.NewReader(rc)
	var manifest []byte
	var foundRootfs bool
	for {
		header, err := tr.Next()
		if err != nil {
			break
		}
		if header.Name == "manifest" {
			buf := bytes.NewBuffer(nil)
			if _, err := io.Copy(buf, tr); err != nil {
				return nil, nil, fmt.Errorf("Failed reading archive: %v", err)
			}
			manifest = buf.Bytes()
		} else if header.Name == "rootfs" {
			foundRootfs = true
		} else if !strings.HasPrefix(header.Name, "rootfs/") {
			return nil, nil, fmt.Errorf("Invalid aci, contains unexpected filename: %q.", header.Name)
		}
	}
	if !foundRootfs {
		return nil, nil, fmt.Errorf("Didn't find rootfs.")
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(manifest); err != nil {
		return nil, nil, fmt.Errorf("Failed to parse manifest: %v", err)
	}
	labels = make(map[string]string)
	for _, label := range im.Labels {
		switch label.Name.String() {
		case "version":
			labels["version"] = label.Value
		case "os":
			labels["os"] = label.Value
		case "arch":
			labels["arch"] = label.Value
		}
	}
	if labels["version"] == "" {
		return nil, nil, fmt.Errorf("Unspecified version is not supported.")
	}
	// if !strings.HasPrefix(im.Name.String(), server+"/") && server != testServer {
	// 	return nil, nil, fmt.Errorf("Image name is %q which is not part of the server %q.", im.Name, server)
	// }
	labels["name"] = im.Name.String()
	data, err = ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, fmt.Errorf("Unable to read file %q: %v", path, err)
	}
	return data, labels, nil
}
Beispiel #5
0
func readManifest(path string) (*schema.ImageManifest, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errorf(err.Error())
	}
	i := schema.ImageManifest{}
	if err := i.UnmarshalJSON(b); err != nil {
		return nil, errorf(err.Error())
	}
	return &i, nil
}
Beispiel #6
0
func prettyPrintMan(manblob []byte) []byte {
	var man schema.ImageManifest

	err := man.UnmarshalJSON(manblob)
	if err != nil {
		panic(err)
	}

	manblob2, err := json.MarshalIndent(man, "", "    ")
	if err != nil {
		panic(err)
	}
	return manblob2
}
Beispiel #7
0
// PrintManifest will print the given manifest to stdout, optionally inserting
// whitespace to make it more human readable.
func PrintManifest(man *schema.ImageManifest, prettyPrint bool) error {
	var manblob []byte
	var err error
	if prettyPrint {
		manblob, err = json.MarshalIndent(man, "", "    ")
	} else {
		manblob, err = man.MarshalJSON()
	}
	if err != nil {
		return err
	}
	fmt.Println(string(manblob))
	return nil
}
Beispiel #8
0
// CatManifest will print to stdout the manifest from the ACI stored at
// aciPath, optionally inserting whitespace to make it more human readable.
func CatManifest(aciPath string, prettyPrint bool) (err error) {
	finfo, err := os.Stat(aciPath)
	switch {
	case os.IsNotExist(err):
		return fmt.Errorf("no such file or directory: %s", aciPath)
	case err != nil:
		return err
	case finfo.IsDir():
		return fmt.Errorf("%s is a directory, not an ACI", aciPath)
	default:
		break
	}

	file, err := os.Open(aciPath)
	if err != nil {
		return err
	}
	defer file.Close()

	tr, err := aci.NewCompressedTarReader(file)
	if err != nil {
		return fmt.Errorf("error decompressing image: %v", err)
	}
	defer tr.Close()

	for {
		hdr, err := tr.Next()
		switch {
		case err == io.EOF:
			return fmt.Errorf("manifest not found in ACI %s", aciPath)
		case err != nil:
			return err
		case hdr.Name == "manifest":
			manblob, err := ioutil.ReadAll(tr)
			if err != nil {
				return err
			}
			var man schema.ImageManifest
			err = man.UnmarshalJSON(manblob)
			if err != nil {
				return err
			}
			return util.PrintManifest(&man, prettyPrint)
		}
	}
}
Beispiel #9
0
// PrepareACIDir takes a manifest and a path to rootfs and lay them out in a
// temp directory that conforms to the layout of ACI image.
func PrepareACIDir(manifest *schema.ImageManifest, rootfs string) (string, error) {
	// Create a temp directory to hold the manifest and rootfs
	tmpDir, err := ioutil.TempDir("", "")
	if err != nil {
		return "", fmt.Errorf("error creating temp directory: %v", err)
	}

	// Write the manifest file
	tmpManifest, err := os.Create(filepath.Join(tmpDir, aci.ManifestFile))
	if err != nil {
		return "", fmt.Errorf("error creating temporary manifest: %v", err)
	}
	defer tmpManifest.Close()

	manifestBytes, err := manifest.MarshalJSON()
	if err != nil {
		return "", fmt.Errorf("error marshalling manifest: %v", err)
	}

	_, err = tmpManifest.Write(manifestBytes)
	if err != nil {
		return "", fmt.Errorf("error writing manifest to temp file: %v", err)
	}
	if err := tmpManifest.Sync(); err != nil {
		return "", fmt.Errorf("error syncing manifest file: %v", err)
	}

	if rootfs == "" {
		// Create an (empty) rootfs
		if err := os.Mkdir(filepath.Join(tmpDir, aci.RootfsDir), 0755); err != nil {
			return "", fmt.Errorf("error making an empty rootfs directory: %v", err)
		}
	} else {
		if err := shutil.CopyTree(rootfs, filepath.Join(tmpDir, aci.RootfsDir), &shutil.CopyTreeOptions{
			Symlinks:               true,
			IgnoreDanglingSymlinks: true,
			CopyFunction:           shutil.Copy,
		}); err != nil {
			return "", fmt.Errorf("Unable to copy rootfs to a temporary directory: %s", err)
		}
	}

	return tmpDir, nil
}
Beispiel #10
0
func checkManifest(t *testing.T, workingDir string, wantedManifest schema.ImageManifest) {
	acipath := path.Join(workingDir, ".acbuild", "currentaci")

	manblob, err := ioutil.ReadFile(path.Join(acipath, aci.ManifestFile))
	if err != nil {
		panic(err)
	}

	var man schema.ImageManifest

	err = man.UnmarshalJSON(manblob)
	if err != nil {
		t.Errorf("invalid manifest schema: %v", err)
	}

	if str := pretty.Compare(man, wantedManifest); str != "" {
		t.Errorf("unexpected manifest:\n%s", str)
	}
}
Beispiel #11
0
func ExtractManifestFromAci(aciPath string) schema.ImageManifest {
	input, err := os.Open(aciPath)
	if err != nil {
		panic("cat-manifest: Cannot open %s: %v" + aciPath + err.Error())
	}
	defer input.Close()

	tr, err := aci.NewCompressedTarReader(input)
	if err != nil {
		panic("cat-manifest: Cannot open tar %s: %v" + aciPath + err.Error())
	}

	im := schema.ImageManifest{}

Tar:
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			break Tar
		case nil:
			if filepath.Clean(hdr.Name) == aci.ManifestFile {
				bytes, err := ioutil.ReadAll(tr)
				if err != nil {
					panic(err)
				}

				err = im.UnmarshalJSON(bytes)
				if err != nil {
					panic(err)
				}
				return im
			}
		default:
			panic("error reading tarball: %v" + err.Error())
		}
	}
	panic("Cannot found manifest if aci")
	return im
}
Beispiel #12
0
Datei: aci.go Projekt: nhlfr/rkt
// NewACI creates a new ACI in the given directory with the given image
// manifest and entries.
// Used for testing.
func NewACI(dir string, manifest string, entries []*ACIEntry) (*os.File, error) {
	var im schema.ImageManifest
	if err := im.UnmarshalJSON([]byte(manifest)); err != nil {
		return nil, errwrap.Wrap(errors.New("invalid image manifest"), err)
	}

	tf, err := ioutil.TempFile(dir, "")
	if err != nil {
		return nil, err
	}
	defer os.Remove(tf.Name())

	tw := tar.NewWriter(tf)
	aw := NewImageWriter(im, tw)

	for _, entry := range entries {
		// Add default mode
		if entry.Header.Mode == 0 {
			if entry.Header.Typeflag == tar.TypeDir {
				entry.Header.Mode = 0755
			} else {
				entry.Header.Mode = 0644
			}
		}
		// Add calling user uid and gid or tests will fail
		entry.Header.Uid = os.Getuid()
		entry.Header.Gid = os.Getgid()
		sr := strings.NewReader(entry.Contents)
		if err := aw.AddFile(entry.Header, sr); err != nil {
			return nil, err
		}
	}

	if err := aw.Close(); err != nil {
		return nil, err
	}
	return tf, nil
}
Beispiel #13
0
Datei: aci.go Projekt: puckel/dgr
func GetDependencyDgrVersion(acName common.ACFullname) (int, error) {
	depFields := data.WithField("dependency", acName.String())

	out, err := Home.Rkt.CatManifest(acName.String())
	if err != nil {
		return 0, errs.WithEF(err, depFields, "Dependency not found")
	}

	im := schema.ImageManifest{}
	if err := im.UnmarshalJSON([]byte(out)); err != nil {
		return 0, errs.WithEF(err, depFields.WithField("content", out), "Cannot read manifest cat by rkt image")
	}

	version, ok := im.Annotations.Get(common.ManifestDrgVersion)
	var val int
	if ok {
		val, err = strconv.Atoi(version)
		if err != nil {
			return 0, errs.WithEF(err, depFields.WithField("version", version), "Failed to parse "+common.ManifestDrgVersion+" from manifest")
		}
	}
	return val, nil
}
Beispiel #14
0
func createACI(dir string, fs map[string]*deb, image string, m *schema.ImageManifest) error {
	idir, err := ioutil.TempDir(dir, "image")
	if err != nil {
		return errorf(err.Error())
	}
	rootfs := filepath.Join(idir, "rootfs")
	os.MkdirAll(rootfs, 0755)

	for _, d := range fs {
		err := run(exec.Command("cp", "-a", d.Path+"/.", rootfs))
		if err != nil {
			return err
		}
		i, err := types.SanitizeACIdentifier(
			fmt.Sprintf("debian.org/deb/%v", d.Name))
		if err != nil {
			return errorf(err.Error())
		}
		a, err := types.NewACIdentifier(i)
		if err != nil {
			return errorf(err.Error())
		}
		m.Annotations.Set(
			*a, fmt.Sprintf("%v/%v", d.Arch, d.Version))
	}
	bytes, err := m.MarshalJSON()
	if err != nil {
		return errorf(err.Error())
	}
	if err := ioutil.WriteFile(filepath.Join(idir, "manifest"), bytes, 0644); err != nil {
		return errorf(err.Error())
	}
	if err := run(exec.Command("actool", "build", "-overwrite", idir, image)); err != nil {
		return err
	}
	return nil
}
Beispiel #15
0
func testCat(t *testing.T, workingDir string) {
	wantedManblob, err := ioutil.ReadFile(path.Join(workingDir, ".acbuild", "currentaci", aci.ManifestFile))
	if err != nil {
		panic(err)
	}
	wantedManblob = append(wantedManblob, byte('\n'))

	var man schema.ImageManifest
	err = man.UnmarshalJSON(wantedManblob)
	if err != nil {
		panic(err)
	}

	_, manblob, _, err := runACBuild(workingDir, "cat-manifest")
	if err != nil {
		t.Fatalf("%v", err)
	}

	if manblob != string(wantedManblob) {
		t.Fatalf("printed manifest and manifest on disk differ")
	}

	wantedManblob = prettyPrintMan(wantedManblob)
	wantedManblob = append(wantedManblob, byte('\n'))

	_, manblob, _, err = runACBuild(workingDir, "cat-manifest", "--pretty-print")
	if err != nil {
		t.Fatalf("%v", err)
	}

	if manblob != string(wantedManblob) {
		t.Fatalf("pretty printed manifest and manifest on disk differ")
	}

	checkManifest(t, workingDir, man)
	checkEmptyRootfs(t, workingDir)
}
Beispiel #16
0
func writeACI(layer io.ReadSeeker, manifest schema.ImageManifest, curPwl []string, output string, compression common.Compression) (*schema.ImageManifest, error) {
	aciFile, err := os.Create(output)
	if err != nil {
		return nil, fmt.Errorf("error creating ACI file: %v", err)
	}
	defer aciFile.Close()

	var w io.WriteCloser = aciFile
	if compression == common.GzipCompression {
		w = gzip.NewWriter(aciFile)
		defer w.Close()
	}
	trw := tar.NewWriter(w)
	defer trw.Close()

	if err := WriteRootfsDir(trw); err != nil {
		return nil, fmt.Errorf("error writing rootfs entry: %v", err)
	}

	fileMap := make(map[string]struct{})
	var whiteouts []string
	convWalker := func(t *tarball.TarFile) error {
		name := t.Name()
		if name == "./" {
			return nil
		}
		t.Header.Name = path.Join("rootfs", name)
		absolutePath := strings.TrimPrefix(t.Header.Name, "rootfs")

		if filepath.Clean(absolutePath) == "/dev" && t.Header.Typeflag != tar.TypeDir {
			return fmt.Errorf(`invalid layer: "/dev" is not a directory`)
		}

		fileMap[absolutePath] = struct{}{}
		if strings.Contains(t.Header.Name, "/.wh.") {
			whiteouts = append(whiteouts, strings.Replace(absolutePath, ".wh.", "", 1))
			return nil
		}
		if t.Header.Typeflag == tar.TypeLink {
			t.Header.Linkname = path.Join("rootfs", t.Linkname())
		}

		if err := trw.WriteHeader(t.Header); err != nil {
			return err
		}
		if _, err := io.Copy(trw, t.TarStream); err != nil {
			return err
		}

		if !util.In(curPwl, absolutePath) {
			curPwl = append(curPwl, absolutePath)
		}

		return nil
	}
	tr, err := aci.NewCompressedTarReader(layer)
	if err == nil {
		defer tr.Close()
		// write files in rootfs/
		if err := tarball.Walk(*tr.Reader, convWalker); err != nil {
			return nil, err
		}
	} else {
		// ignore errors: empty layers in tars generated by docker save are not
		// valid tar files so we ignore errors trying to open them. Converted
		// ACIs will have the manifest and an empty rootfs directory in any
		// case.
	}
	newPwl := subtractWhiteouts(curPwl, whiteouts)

	newPwl, err = writeStdioSymlinks(trw, fileMap, newPwl)
	if err != nil {
		return nil, err
	}
	// Let's copy the newly generated PathWhitelist to avoid unintended
	// side-effects
	manifest.PathWhitelist = make([]string, len(newPwl))
	copy(manifest.PathWhitelist, newPwl)

	if err := WriteManifest(trw, manifest); err != nil {
		return nil, fmt.Errorf("error writing manifest: %v", err)
	}

	return &manifest, nil
}
Beispiel #17
0
func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	if patchExec != "" {
		im.App.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" {
		im.App.User = patchUser
	}
	if patchGroup != "" {
		im.App.Group = patchGroup
	}

	var app *types.App
	if patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" {
		app = im.App
		if app == nil {
			return fmt.Errorf("no app in the manifest")
		}
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.

		// TODO: Instead of creating a JSON and then unmarshalling it, the isolator
		// should be instantiated directory. But it requires a constructor, see:
		// https://github.com/appc/spec/issues/268
		capsList := strings.Split(patchCaps, ",")
		caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`))
		isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps)
		isolator = &types.Isolator{}
		err := isolator.UnmarshalJSON([]byte(isolatorStr))
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		app.Isolators = append(app.Isolators, *isolator)
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			if _, ok := types.ResourceIsolatorNames[name]; !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}
Beispiel #18
0
func genManifest(path string) *schema.ImageManifest {
	// Get runtime.json and config.json
	runtimePath := path + "/runtime.json"
	configPath := path + "/config.json"

	runtime, err := ioutil.ReadFile(runtimePath)
	if err != nil {
		if debugEnabled {
			log.Printf("Open file runtime.json failed: %v", err)
		}
		return nil
	}

	config, err := ioutil.ReadFile(configPath)
	if err != nil {
		if debugEnabled {
			log.Printf("Open file config.json failed: %v", err)
		}
		return nil
	}

	var spec specs.LinuxSpec
	err = json.Unmarshal(config, &spec)
	if err != nil {
		if debugEnabled {
			log.Printf("Unmarshal config.json failed: %v", err)
		}
		return nil
	}

	var runSpec specs.LinuxRuntimeSpec
	err = json.Unmarshal(runtime, &runSpec)
	if err != nil {
		if debugEnabled {
			log.Printf("Unmarshal runtime.json failed: %v", err)
		}
		return nil
	}
	// Begin to convert runtime.json/config.json to manifest
	m := new(schema.ImageManifest)

	// 1. Assemble "acKind" field
	m.ACKind = schema.ImageManifestKind

	// 2. Assemble "acVersion" field
	m.ACVersion = schema.AppContainerVersion

	// 3. Assemble "name" field
	m.Name = types.ACIdentifier(manifestName)

	// 4. Assemble "labels" field
	// 4.1 "version"
	label := new(types.Label)
	label.Name = types.ACIdentifier("version")
	label.Value = spec.Version
	m.Labels = append(m.Labels, *label)
	// 4.2 "os"
	label = new(types.Label)
	label.Name = types.ACIdentifier("os")
	label.Value = spec.Platform.OS
	m.Labels = append(m.Labels, *label)
	// 4.3 "arch"
	label = new(types.Label)
	label.Name = types.ACIdentifier("arch")
	label.Value = spec.Platform.Arch
	m.Labels = append(m.Labels, *label)

	// 5. Assemble "app" field
	app := new(types.App)
	// 5.1 "exec"
	app.Exec = spec.Process.Args

	prefixDir := ""
	//var exeStr string
	if app.Exec == nil {
		app.Exec = append(app.Exec, "/bin/sh")
	} else {
		if !filepath.IsAbs(app.Exec[0]) {
			if spec.Process.Cwd == "" {
				prefixDir = "/"
			} else {
				prefixDir = spec.Process.Cwd
			}
		}
		app.Exec[0] = prefixDir + app.Exec[0]
	}

	// 5.2 "user"
	app.User = fmt.Sprintf("%d", spec.Process.User.UID)
	// 5.3 "group"
	app.Group = fmt.Sprintf("%d", spec.Process.User.GID)
	// 5.4 "eventHandlers"
	event := new(types.EventHandler)
	event.Name = "pre-start"
	for index := range runSpec.Hooks.Prestart {
		event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Path)
		event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Args...)
		event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Env...)
	}
	if len(event.Exec) == 0 {
		event.Exec = append(event.Exec, "/bin/echo")
		event.Exec = append(event.Exec, "-n")
	}
	app.EventHandlers = append(app.EventHandlers, *event)
	event = new(types.EventHandler)
	event.Name = "post-stop"
	for index := range runSpec.Hooks.Poststop {
		event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Path)
		event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Args...)
		event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Env...)
	}
	if len(event.Exec) == 0 {
		event.Exec = append(event.Exec, "/bin/echo")
		event.Exec = append(event.Exec, "-n")
	}
	app.EventHandlers = append(app.EventHandlers, *event)
	// 5.5 "workingDirectory"
	app.WorkingDirectory = spec.Process.Cwd
	// 5.6 "environment"
	env := new(types.EnvironmentVariable)
	for index := range spec.Process.Env {
		s := strings.Split(spec.Process.Env[index], "=")
		env.Name = s[0]
		env.Value = s[1]
		app.Environment = append(app.Environment, *env)
	}

	// 5.7 "mountPoints"
	for index := range spec.Mounts {
		mount := new(types.MountPoint)
		mount.Name = types.ACName(spec.Mounts[index].Name)
		mount.Path = spec.Mounts[index].Path
		mount.ReadOnly = false
		app.MountPoints = append(app.MountPoints, *mount)
	}

	// 5.8 "ports"

	// 5.9 "isolators"
	if runSpec.Linux.Resources != nil {
		if runSpec.Linux.Resources.CPU.Quota != 0 {
			cpuLimt := new(ResourceCPU)
			cpuLimt.Limit = fmt.Sprintf("%dm", runSpec.Linux.Resources.CPU.Quota)
			isolator := new(types.Isolator)
			isolator.Name = types.ACIdentifier("resource/cpu")
			bytes, _ := json.Marshal(cpuLimt)

			valueRaw := json.RawMessage(bytes)
			isolator.ValueRaw = &valueRaw

			app.Isolators = append(app.Isolators, *isolator)
		}
		if runSpec.Linux.Resources.Memory.Limit != 0 {
			memLimt := new(ResourceMem)
			memLimt.Limit = fmt.Sprintf("%dG", runSpec.Linux.Resources.Memory.Limit/(1024*1024*1024))
			isolator := new(types.Isolator)
			isolator.Name = types.ACIdentifier("resource/memory")
			bytes, _ := json.Marshal(memLimt)

			valueRaw := json.RawMessage(bytes)
			isolator.ValueRaw = &valueRaw

			app.Isolators = append(app.Isolators, *isolator)
		}
	}

	if len(spec.Linux.Capabilities) != 0 {
		isolatorCapSet := new(IsolatorCapSet)
		isolatorCapSet.Sets = append(isolatorCapSet.Sets, spec.Linux.Capabilities...)

		isolator := new(types.Isolator)
		isolator.Name = types.ACIdentifier(types.LinuxCapabilitiesRetainSetName)
		bytes, _ := json.Marshal(isolatorCapSet)

		valueRaw := json.RawMessage(bytes)
		isolator.ValueRaw = &valueRaw

		app.Isolators = append(app.Isolators, *isolator)
	}

	// 6. "annotations"

	// 7. "dependencies"

	// 8. "pathWhitelist"

	m.App = app

	return m
}
Beispiel #19
0
func runValidate(args []string) (exit int) {
	if len(args) < 1 {
		stderr("must pass one or more files")
		return 1
	}

	for _, path := range args {
		vt := valType
		fi, err := os.Stat(path)
		if err != nil {
			stderr("unable to access %s: %v", path, err)
			return 1
		}
		var fh *os.File
		if fi.IsDir() {
			switch vt {
			case typeImageLayout:
			case "":
				vt = typeImageLayout
			case typeManifest, typeAppImage:
				stderr("%s is a directory (wrong --type?)", path)
				return 1
			default:
				// should never happen
				panic(fmt.Sprintf("unexpected type: %v", vt))
			}
		} else {
			fh, err = os.Open(path)
			if err != nil {
				stderr("%s: unable to open: %v", path, err)
				return 1
			}
		}

		if vt == "" {
			vt, err = detectValType(fh)
			if err != nil {
				stderr("%s: error detecting file type: %v", path, err)
				return 1
			}
		}
		switch vt {
		case typeImageLayout:
			err = aci.ValidateLayout(path)
			if err != nil {
				stderr("%s: invalid image layout: %v", path, err)
				exit = 1
			} else if globalFlags.Debug {
				stderr("%s: valid image layout", path)
			}
		case typeAppImage:
			tr, err := aci.NewCompressedTarReader(fh)
			if err != nil {
				stderr("%s: error decompressing file: %v", path, err)
				return 1
			}
			err = aci.ValidateArchive(tr.Reader)
			tr.Close()
			fh.Close()
			if err != nil {
				if e, ok := err.(aci.ErrOldVersion); ok {
					stderr("%s: warning: %v", path, e)
				} else {
					stderr("%s: error validating: %v", path, err)
					exit = 1
				}
			} else if globalFlags.Debug {
				stderr("%s: valid app container image", path)
			}
		case typeManifest:
			b, err := ioutil.ReadAll(fh)
			fh.Close()
			if err != nil {
				stderr("%s: unable to read file %s", path, err)
				return 1
			}
			k := schema.Kind{}
			if err := k.UnmarshalJSON(b); err != nil {
				stderr("%s: error unmarshaling manifest: %v", path, err)
				return 1
			}
			switch k.ACKind {
			case "ImageManifest":
				m := schema.ImageManifest{}
				err = m.UnmarshalJSON(b)
			case "PodManifest":
				m := schema.PodManifest{}
				err = m.UnmarshalJSON(b)
			default:
				// Should not get here; schema.Kind unmarshal should fail
				panic("bad ACKind")
			}
			if err != nil {
				stderr("%s: invalid %s: %v", path, k.ACKind, err)
				exit = 1
			} else if globalFlags.Debug {
				stderr("%s: valid %s", path, k.ACKind)
			}
		default:
			stderr("%s: unable to detect filetype (try --type)", path)
			return 1
		}
	}

	return
}
Beispiel #20
0
func writeACI(layer io.ReadSeeker, manifest schema.ImageManifest, curPwl []string, output string, compress bool) (*schema.ImageManifest, error) {
	aciFile, err := os.Create(output)
	if err != nil {
		return nil, fmt.Errorf("error creating ACI file: %v", err)
	}
	defer aciFile.Close()

	var w io.WriteCloser = aciFile
	if compress {
		w = gzip.NewWriter(aciFile)
		defer w.Close()
	}
	trw := tar.NewWriter(w)
	defer trw.Close()

	if err := WriteRootfsDir(trw); err != nil {
		return nil, fmt.Errorf("error writing rootfs entry: %v", err)
	}

	var whiteouts []string
	convWalker := func(t *tarball.TarFile) error {
		name := t.Name()
		if name == "./" {
			return nil
		}
		t.Header.Name = path.Join("rootfs", name)
		absolutePath := strings.TrimPrefix(t.Header.Name, "rootfs")
		if strings.Contains(t.Header.Name, "/.wh.") {
			whiteouts = append(whiteouts, strings.Replace(absolutePath, ".wh.", "", 1))
			return nil
		}
		if t.Header.Typeflag == tar.TypeLink {
			t.Header.Linkname = path.Join("rootfs", t.Linkname())
		}

		if err := trw.WriteHeader(t.Header); err != nil {
			return err
		}
		if _, err := io.Copy(trw, t.TarStream); err != nil {
			return err
		}

		if !util.In(curPwl, absolutePath) {
			curPwl = append(curPwl, absolutePath)
		}

		return nil
	}
	tr, err := aci.NewCompressedTarReader(layer)
	if err == nil {
		defer tr.Close()
		// write files in rootfs/
		if err := tarball.Walk(*tr.Reader, convWalker); err != nil {
			return nil, err
		}
	} else {
		// ignore errors: empty layers in tars generated by docker save are not
		// valid tar files so we ignore errors trying to open them. Converted
		// ACIs will have the manifest and an empty rootfs directory in any
		// case.
	}
	newPwl := subtractWhiteouts(curPwl, whiteouts)

	manifest.PathWhitelist = newPwl
	if err := WriteManifest(trw, manifest); err != nil {
		return nil, fmt.Errorf("error writing manifest: %v", err)
	}

	return &manifest, nil
}
Beispiel #21
0
func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	var app *types.App = im.App
	if patchExec != "" {
		if app == nil {
			// if the original manifest was missing an app and
			// patchExec is set let's assume the user is trying to
			// inject one...
			im.App = &types.App{}
			app = im.App
		}
		app.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" ||
		patchGroup != "" ||
		patchSupplementaryGIDs != "" ||
		patchCaps != "" ||
		patchRevokeCaps != "" ||
		patchMounts != "" ||
		patchPorts != "" ||
		patchIsolators != "" {
		// ...but if we still don't have an app and the user is trying
		// to patch one of its other parameters, it's an error
		if app == nil {
			return fmt.Errorf("no app in the supplied manifest and no exec command provided")
		}
	}

	if patchUser != "" {
		app.User = patchUser
	}

	if patchGroup != "" {
		app.Group = patchGroup
	}

	if patchSupplementaryGIDs != "" {
		app.SupplementaryGIDs = []int{}
		gids := strings.Split(patchSupplementaryGIDs, ",")
		for _, g := range gids {
			gid, err := strconv.Atoi(g)
			if err != nil {
				return fmt.Errorf("invalid supplementary group %q: %v", g, err)
			}
			app.SupplementaryGIDs = append(app.SupplementaryGIDs, gid)
		}
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists (os/linux/capabilities-retain-set)")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.
		caps, err := types.NewLinuxCapabilitiesRetainSet(strings.Split(patchCaps, ",")...)
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		app.Isolators = append(app.Isolators, caps.AsIsolator())
	}
	if patchRevokeCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRevokeSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists (os/linux/capabilities-remove-set)")
		}

		// Instantiate a Isolator with the content specified by the --revoke-capability
		// parameter.
		caps, err := types.NewLinuxCapabilitiesRevokeSet(strings.Split(patchRevokeCaps, ",")...)
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchRevokeCaps, err)
		}
		app.Isolators = append(app.Isolators, caps.AsIsolator())
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			if _, ok := types.ResourceIsolatorNames[name]; !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}
Beispiel #22
0
func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	var app *types.App = im.App
	if patchExec != "" {
		if app == nil {
			// if the original manifest was missing an app and
			// patchExec is set let's assume the user is trying to
			// inject one...
			im.App = &types.App{}
			app = im.App
		}
		app.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" || patchGroup != "" || patchSupplementaryGIDs != "" || patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" {
		// ...but if we still don't have an app and the user is trying
		// to patch one of its other parameters, it's an error
		if app == nil {
			return fmt.Errorf("no app in the supplied manifest and no exec command provided")
		}
	}

	if patchUser != "" {
		app.User = patchUser
	}

	if patchGroup != "" {
		app.Group = patchGroup
	}

	if patchSupplementaryGIDs != "" {
		app.SupplementaryGIDs = []int{}
		gids := strings.Split(patchSupplementaryGIDs, ",")
		for _, g := range gids {
			gid, err := strconv.Atoi(g)
			if err != nil {
				return fmt.Errorf("invalid supplementary group %q: %v", g, err)
			}
			app.SupplementaryGIDs = append(app.SupplementaryGIDs, gid)
		}
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.

		// TODO: Instead of creating a JSON and then unmarshalling it, the isolator
		// should be instantiated directory. But it requires a constructor, see:
		// https://github.com/appc/spec/issues/268
		capsList := strings.Split(patchCaps, ",")
		caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`))
		isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps)
		isolator = &types.Isolator{}
		err := isolator.UnmarshalJSON([]byte(isolatorStr))
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		app.Isolators = append(app.Isolators, *isolator)
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			if _, ok := types.ResourceIsolatorNames[name]; !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}
Beispiel #23
0
func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	var app *types.App = im.App
	if patchExec != "" {
		if app == nil {
			// if the original manifest was missing an app and
			// patchExec is set let's assume the user is trying to
			// inject one...
			im.App = &types.App{}
			app = im.App
		}
		app.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" ||
		patchGroup != "" ||
		patchSupplementaryGIDs != "" ||
		patchCaps != "" ||
		patchRevokeCaps != "" ||
		patchMounts != "" ||
		patchPorts != "" ||
		patchIsolators != "" {
		// ...but if we still don't have an app and the user is trying
		// to patch one of its other parameters, it's an error
		if app == nil {
			return fmt.Errorf("no app in the supplied manifest and no exec command provided")
		}
	}

	if patchUser != "" {
		app.User = patchUser
	}

	if patchGroup != "" {
		app.Group = patchGroup
	}

	if patchSupplementaryGIDs != "" {
		app.SupplementaryGIDs = []int{}
		gids := strings.Split(patchSupplementaryGIDs, ",")
		for _, g := range gids {
			gid, err := strconv.Atoi(g)
			if err != nil {
				return fmt.Errorf("invalid supplementary group %q: %v", g, err)
			}
			app.SupplementaryGIDs = append(app.SupplementaryGIDs, gid)
		}
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists (os/linux/capabilities-retain-set)")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.
		caps, err := types.NewLinuxCapabilitiesRetainSet(strings.Split(patchCaps, ",")...)
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		isolator, err = caps.AsIsolator()
		if err != nil {
			return err
		}
		app.Isolators = append(app.Isolators, *isolator)
	}
	if patchRevokeCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRevokeSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists (os/linux/capabilities-remove-set)")
		}

		// Instantiate a Isolator with the content specified by the --revoke-capability
		// parameter.
		caps, err := types.NewLinuxCapabilitiesRevokeSet(strings.Split(patchRevokeCaps, ",")...)
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchRevokeCaps, err)
		}
		isolator, err = caps.AsIsolator()
		if err != nil {
			return err
		}
		app.Isolators = append(app.Isolators, *isolator)
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	// Parse seccomp args and override existing seccomp isolators
	if patchSeccompMode != "" {
		seccompIsolator, err := parseSeccompArgs(patchSeccompMode, patchSeccompSet)
		if err != nil {
			return err
		}
		seccompReps := []types.ACIdentifier{types.LinuxSeccompRemoveSetName, types.LinuxSeccompRetainSetName}
		app.Isolators.ReplaceIsolatorsByName(*seccompIsolator, seccompReps)
	} else if patchSeccompSet != "" {
		return fmt.Errorf("--seccomp-set specified without --seccomp-mode")
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			_, ok := types.ResourceIsolatorNames[name]

			switch name {
			case types.LinuxNoNewPrivilegesName:
				ok = true
				kv := strings.Split(is, ",")
				if len(kv) != 2 {
					return fmt.Errorf("isolator %s: invalid format", name)
				}
				isolatorStr = fmt.Sprintf(`{ "name": "%s", "value": %s }`, name, kv[1])
			case types.LinuxSeccompRemoveSetName, types.LinuxSeccompRetainSetName:
				ok = false
			}

			if !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}
Beispiel #24
0
func BasicImageManifest() *schema.ImageManifest {
	im := new(schema.ImageManifest)
	im.UnmarshalJSON([]byte(IMAGE_MANIFEST))
	return im
}
Beispiel #25
0
func runBuild(args []string) (exit int) {
	if len(args) != 2 {
		stderr("build: Must provide directory and output file")
		return 1
	}

	root := args[0]
	tgt := args[1]
	ext := filepath.Ext(tgt)
	if ext != schema.ACIExtension {
		stderr("build: Extension must be %s (given %s)", schema.ACIExtension, ext)
		return 1
	}

	mode := os.O_CREATE | os.O_WRONLY
	if buildOverwrite {
		mode |= os.O_TRUNC
	} else {
		mode |= os.O_EXCL
	}
	fh, err := os.OpenFile(tgt, mode, 0644)
	if err != nil {
		if os.IsExist(err) {
			stderr("build: Target file exists (try --overwrite)")
		} else {
			stderr("build: Unable to open target %s: %v", tgt, err)
		}
		return 1
	}

	var gw *gzip.Writer
	var r io.WriteCloser = fh
	if !buildNocompress {
		gw = gzip.NewWriter(fh)
		r = gw
	}
	tr := tar.NewWriter(r)

	defer func() {
		tr.Close()
		if !buildNocompress {
			gw.Close()
		}
		fh.Close()
		if exit != 0 && !buildOverwrite {
			os.Remove(tgt)
		}
	}()

	// TODO(jonboulle): stream the validation so we don't have to walk the rootfs twice
	if err := aci.ValidateLayout(root); err != nil {
		stderr("build: Layout failed validation: %v", err)
		return 1
	}
	mpath := filepath.Join(root, aci.ManifestFile)
	b, err := ioutil.ReadFile(mpath)
	if err != nil {
		stderr("build: Unable to read Image Manifest: %v", err)
		return 1
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		stderr("build: Unable to load Image Manifest: %v", err)
		return 1
	}
	iw := aci.NewImageWriter(im, tr)

	err = filepath.Walk(root, aci.BuildWalker(root, iw))
	if err != nil {
		stderr("build: Error walking rootfs: %v", err)
		return 1
	}

	err = iw.Close()
	if err != nil {
		stderr("build: Unable to close image %s: %v", tgt, err)
		return 1
	}

	return
}
Beispiel #26
0
// buildAci builds a target aci from the root directory using any uid shift
// information from uidRange.
func buildAci(root, manifestPath, target string, uidRange *user.UidRange) (e error) {
	mode := os.O_CREATE | os.O_WRONLY
	if flagOverwriteACI {
		mode |= os.O_TRUNC
	} else {
		mode |= os.O_EXCL
	}
	aciFile, err := os.OpenFile(target, mode, 0644)
	if err != nil {
		if os.IsExist(err) {
			return errors.New("target file exists (try --overwrite)")
		} else {
			return errwrap.Wrap(fmt.Errorf("unable to open target %s", target), err)
		}
	}

	gw := gzip.NewWriter(aciFile)
	tr := tar.NewWriter(gw)

	defer func() {
		tr.Close()
		gw.Close()
		aciFile.Close()
		// e is implicitly assigned by the return statement. As defer runs
		// after return, but before actually returning, this works.
		if e != nil {
			os.Remove(target)
		}
	}()

	b, err := ioutil.ReadFile(manifestPath)
	if err != nil {
		return errwrap.Wrap(errors.New("unable to read Image Manifest"), err)
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		return errwrap.Wrap(errors.New("unable to load Image Manifest"), err)
	}
	iw := aci.NewImageWriter(im, tr)

	// Unshift uid and gid when pod was started with --private-user (user namespace)
	var walkerCb aci.TarHeaderWalkFunc = func(hdr *tar.Header) bool {
		if uidRange != nil {
			uid, gid, err := uidRange.UnshiftRange(uint32(hdr.Uid), uint32(hdr.Gid))
			if err != nil {
				stderr.PrintE("error unshifting gid and uid", err)
				return false
			}
			hdr.Uid, hdr.Gid = int(uid), int(gid)
		}
		return true
	}

	if err := filepath.Walk(root, aci.BuildWalker(root, iw, walkerCb)); err != nil {
		return errwrap.Wrap(errors.New("error walking rootfs"), err)
	}

	if err = iw.Close(); err != nil {
		return errwrap.Wrap(fmt.Errorf("unable to close image %s", target), err)
	}

	return
}
Beispiel #27
0
// BuildACI takes an input directory that conforms to the ACI specification,
// and outputs an optionally compressed ACI image.
func BuildACI(root string, tgt string, overwrite bool, nocompress bool) (ret error) {
	ext := filepath.Ext(tgt)
	if ext != schema.ACIExtension {
		ret = fmt.Errorf("build: Extension must be %s (given %s)", schema.ACIExtension, ext)
		return
	}

	mode := os.O_CREATE | os.O_WRONLY
	if overwrite {
		mode |= os.O_TRUNC
	} else {
		mode |= os.O_EXCL
	}
	fh, err := os.OpenFile(tgt, mode, 0644)
	if err != nil {
		if os.IsExist(err) {
			ret = fmt.Errorf("build: Target file exists")
		} else {
			ret = fmt.Errorf("build: Unable to open target %s: %v", tgt, err)
		}
		return
	}

	var gw *gzip.Writer
	var r io.WriteCloser = fh
	if !nocompress {
		gw = gzip.NewWriter(fh)
		r = gw
	}
	tr := tar.NewWriter(r)

	defer func() {
		tr.Close()
		if !nocompress {
			gw.Close()
		}
		fh.Close()
		if ret != nil && !overwrite {
			os.Remove(tgt)
		}
	}()

	// TODO(jonboulle): stream the validation so we don't have to walk the rootfs twice
	if err := aci.ValidateLayout(root); err != nil {
		ret = fmt.Errorf("build: Layout failed validation: %v", err)
		return
	}
	mpath := filepath.Join(root, aci.ManifestFile)
	b, err := ioutil.ReadFile(mpath)
	if err != nil {
		ret = fmt.Errorf("build: Unable to read Image Manifest: %v", err)
		return
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		ret = fmt.Errorf("build: Unable to load Image Manifest: %v", err)
		return
	}
	iw := aci.NewImageWriter(im, tr)

	err = filepath.Walk(root, aci.BuildWalker(root, iw))
	if err != nil {
		ret = fmt.Errorf("build: Error walking rootfs: %v", err)
		return
	}

	err = iw.Close()
	if err != nil {
		ret = fmt.Errorf("build: Unable to close image %s: %v", tgt, err)
		return
	}

	return nil
}
Beispiel #28
0
func (aci *Aci) prepareStage1aci() (string, error) {
	ImportInternalBuilderIfNeeded(aci.manifest)
	if len(aci.manifest.Builder.Dependencies) == 0 {
		return "", nil
	}

	logs.WithFields(aci.fields).Debug("Preparing stage1")

	if err := os.MkdirAll(aci.target+pathStage1+common.PathRootfs, 0777); err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("path", aci.target+pathBuilder), "Failed to create stage1 aci path")
	}

	Home.Rkt.Fetch(aci.manifest.Builder.Image.String())
	manifestStr, err := Home.Rkt.CatManifest(aci.manifest.Builder.Image.String())
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to read stage1 image manifest")
	}

	manifest := schema.ImageManifest{}
	if err := json.Unmarshal([]byte(manifestStr), &manifest); err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("content", manifestStr), "Failed to unmarshal stage1 manifest received from rkt")
	}

	manifest.Dependencies = types.Dependencies{}
	dep, err := common.ToAppcDependencies(aci.manifest.Builder.Dependencies)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Invalid dependency on stage1 for rkt")
	}
	manifest.Dependencies = append(manifest.Dependencies, dep...)

	stage1Image, err := common.ToAppcDependencies([]common.ACFullname{aci.manifest.Builder.Image})
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Invalid image on stage1 for rkt")
	}
	manifest.Dependencies = append(manifest.Dependencies, stage1Image...)

	name, err := types.NewACIdentifier(prefixBuilderStage1 + aci.manifest.NameAndVersion.Name())
	if err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("name", prefixBuilderStage1+aci.manifest.NameAndVersion.Name()),
			"aci name is not a valid identifier for rkt")
	}
	manifest.Name = *name

	content, err := json.MarshalIndent(&manifest, "", "  ")
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to marshal builder's stage1 manifest")
	}

	if err := ioutil.WriteFile(aci.target+pathStage1+common.PathManifest, content, 0644); err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("path", aci.target+pathStage1+common.PathManifest),
			"Failed to write builder's stage1 manifest to file")
	}

	if err := aci.tarAci(aci.target + pathStage1); err != nil {
		return "", err
	}

	logs.WithF(aci.fields.WithField("path", aci.target+pathStage1+pathImageAci)).Info("Importing builder's stage1")
	hash, err := Home.Rkt.FetchInsecure(aci.target + pathStage1 + pathImageAci)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "fetch of builder's stage1 aci failed")
	}
	return hash, nil
}
Beispiel #29
0
func createACI(dir string, imageName string) error {
	var errStr string
	var errRes error
	buildNocompress := true
	root := dir
	tgt := imageName

	ext := filepath.Ext(tgt)
	if ext != schema.ACIExtension {
		errStr = fmt.Sprintf("build: Extension must be %s (given %s)", schema.ACIExtension, ext)
		errRes = errors.New(errStr)
		return errRes
	}

	if err := aci.ValidateLayout(root); err != nil {
		if e, ok := err.(aci.ErrOldVersion); ok {
			if debugEnabled {
				log.Printf("build: Warning: %v. Please update your manifest.", e)
			}
		} else {
			errStr = fmt.Sprintf("build: Layout failed validation: %v", err)
			errRes = errors.New(errStr)
			return errRes
		}
	}

	mode := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
	fh, err := os.OpenFile(tgt, mode, 0644)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to open target %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	var gw *gzip.Writer
	var r io.WriteCloser = fh
	if !buildNocompress {
		gw = gzip.NewWriter(fh)
		r = gw
	}
	tr := tar.NewWriter(r)

	defer func() {
		tr.Close()
		if !buildNocompress {
			gw.Close()
		}
		fh.Close()
	}()

	mpath := filepath.Join(root, aci.ManifestFile)
	b, err := ioutil.ReadFile(mpath)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to read Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		errStr = fmt.Sprintf("build: Unable to load Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	iw := aci.NewImageWriter(im, tr)

	err = filepath.Walk(root, aci.BuildWalker(root, iw, nil))
	if err != nil {
		errStr = fmt.Sprintf("build: Error walking rootfs: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}

	err = iw.Close()
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to close image %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	return nil
}