func runCatManifest(args []string) (exit int) { if len(args) != 1 { stderr("cat-manifest: Must provide one file") return 1 } inputFile = args[0] input, err := os.Open(inputFile) if err != nil { stderr("cat-manifest: Cannot open %s: %v", inputFile, err) return 1 } defer input.Close() tr, err := aci.NewCompressedTarReader(input) if err != nil { stderr("cat-manifest: Cannot extract %s: %v", inputFile, err) return 1 } defer tr.Close() err = extractManifest(tr.Reader, nil, true, nil) if err != nil { stderr("cat-manifest: Unable to read %s: %v", inputFile, err) return 1 } return }
func ValidateACI(aciPath string) error { aciFile, err := os.Open(aciPath) if err != nil { return err } defer aciFile.Close() reader, err := aci.NewCompressedTarReader(aciFile) if err != nil { return err } if err := aci.ValidateArchive(reader); err != nil { return err } return nil }
func runPatchManifest(args []string) (exit int) { var fh *os.File var err error if patchReplace && patchOverwrite { stderr("patch-manifest: Cannot use both --replace and --overwrite") return 1 } if !patchReplace && len(args) != 2 { stderr("patch-manifest: Must provide input and output files (or use --replace)") return 1 } if patchReplace && len(args) != 1 { stderr("patch-manifest: Must provide one file") return 1 } if patchManifestFile != "" && (patchName != "" || patchExec != "" || patchUser != "" || patchGroup != "" || patchCaps != "" || patchMounts != "") { stderr("patch-manifest: --manifest is incompatible with other manifest editing options") return 1 } inputFile = args[0] // Prepare output writer if patchReplace { fh, err = ioutil.TempFile(path.Dir(inputFile), ".actool-tmp."+path.Base(inputFile)+"-") if err != nil { stderr("patch-manifest: Cannot create temporary file: %v", err) return 1 } } else { outputFile = args[1] ext := filepath.Ext(outputFile) if ext != schema.ACIExtension { stderr("patch-manifest: Extension must be %s (given %s)", schema.ACIExtension, ext) return 1 } mode := os.O_CREATE | os.O_WRONLY if patchOverwrite { mode |= os.O_TRUNC } else { mode |= os.O_EXCL } fh, err = os.OpenFile(outputFile, mode, 0644) if err != nil { if os.IsExist(err) { stderr("patch-manifest: Output file exists (try --overwrite)") } else { stderr("patch-manifest: Unable to open output %s: %v", outputFile, err) } return 1 } } var gw *gzip.Writer var w io.WriteCloser = fh if !patchNocompress { gw = gzip.NewWriter(fh) w = gw } tw := tar.NewWriter(w) defer func() { tw.Close() if !patchNocompress { gw.Close() } fh.Close() if exit != 0 && !patchOverwrite { os.Remove(fh.Name()) } }() // Prepare input reader input, err := os.Open(inputFile) if err != nil { stderr("patch-manifest: Cannot open %s: %v", inputFile, err) return 1 } defer input.Close() tr, err := aci.NewCompressedTarReader(input) if err != nil { stderr("patch-manifest: Cannot extract %s: %v", inputFile, err) return 1 } defer tr.Close() var newManifest []byte if patchManifestFile != "" { mr, err := os.Open(patchManifestFile) if err != nil { stderr("patch-manifest: Cannot open %s: %v", patchManifestFile, err) return 1 } defer input.Close() newManifest, err = ioutil.ReadAll(mr) if err != nil { stderr("patch-manifest: Cannot read %s: %v", patchManifestFile, err) return 1 } } err = extractManifest(tr.Reader, tw, false, newManifest) if err != nil { stderr("patch-manifest: Unable to read %s: %v", inputFile, err) return 1 } if patchReplace { err = os.Rename(fh.Name(), inputFile) if err != nil { stderr("patch-manifest: Cannot rename %q to %q: %v", fh.Name, inputFile, err) return 1 } } return }
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 }
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 } reader, err := aci.NewCompressedTarReader(layer) if err == nil { // write files in rootfs/ if err := tarball.Walk(*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 }