// TODO(jonboulle): find a cleaner way to communicate instead of all these args. func validate(amOK bool, am io.Reader, fsmOK bool, fsm io.Reader, rfsOK bool, files []string) error { if !amOK && !fsmOK { return ErrNoManifest } if amOK { if !rfsOK { return ErrNoRootFS } b, err := ioutil.ReadAll(am) if err != nil { return fmt.Errorf("error reading app manifest: %v", err) } var a schema.AppManifest if err := a.UnmarshalJSON(b); err != nil { return fmt.Errorf("app manifest validation failed: %v", err) } } var rfsfiles []string for _, f := range files { switch { case strings.HasPrefix(f, "rootfs"): rfsfiles = append(rfsfiles, strings.TrimPrefix(f, "rootfs")) default: return fmt.Errorf("unrecognized file path in layout: %q", f) } } if fsmOK { b, err := ioutil.ReadAll(fsm) if err != nil { return fmt.Errorf("error reading fileset manifest: %v", err) } var f schema.FilesetManifest if err := f.UnmarshalJSON(b); err != nil { return fmt.Errorf("fileset manifest validation failed: %v", err) } // TODO(jonboulle): this is not quite correct since it does not // deal with dependent filesets. Maybe filesAreSuperset()? return filesEqual(f.Files, rfsfiles) } return nil }
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: fr, err := maybeDecompress(fh) if err != nil { stderr("%s: error decompressing file: %v", path, err) return 1 } tr := tar.NewReader(fr) err = aci.ValidateArchive(tr) fh.Close() if err != nil { 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 "AppManifest": m := schema.AppManifest{} err = m.UnmarshalJSON(b) case "ContainerRuntimeManifest": m := schema.ContainerRuntimeManifest{} err = m.UnmarshalJSON(b) case "FilesetManifest": m := schema.FilesetManifest{} 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 runBuild(args []string) (exit int) { if len(args) != 2 { stderr("build: Must provide directory and output file") return 1 } switch { case buildFilesetName != "" && buildAppManifest == "": case buildFilesetName == "" && buildAppManifest != "": default: stderr("build: must specify either --fileset-name or --app-manifest") 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_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 } gw := gzip.NewWriter(fh) tr := tar.NewWriter(gw) defer func() { tr.Close() gw.Close() fh.Close() if exit != 0 && !buildOverwrite { os.Remove(tgt) } }() var aw aci.ArchiveWriter if buildFilesetName != "" { aw, err = aci.NewFilesetWriter(buildFilesetName, tr) if err != nil { stderr("build: Unable to create FilesetWriter: %v", err) return 1 } } else { b, err := ioutil.ReadFile(buildAppManifest) if err != nil { stderr("build: Unable to read App Manifest: %v", err) return 1 } var am schema.AppManifest if err := am.UnmarshalJSON(b); err != nil { stderr("build: Unable to load App Manifest: %v", err) return 1 } aw = aci.NewAppWriter(am, tr) } err = filepath.Walk(root, buildWalker(root, aw, buildRootfs)) if err != nil { stderr("build: Error walking rootfs: %v", err) return 1 } err = aw.Close() if err != nil { stderr("build: Unable to close Fileset image %s: %v", tgt, err) return 1 } return }