func runRender(context *cli.Context) { s := getStore() args := context.Args() if len(args) < 2 { fmt.Println("There need to be at least two arguments.") fmt.Println(context.Command.Usage) return } in := args[0] out := args[1] // Render the given image in tree store imageHash, err := renderInStore(s, in) if err != nil { log.Fatalf("error rendering image in store: %s", err) } imagePath := s.GetTreeStorePath(imageHash) if err := shutil.CopyTree(imagePath, out, &shutil.CopyTreeOptions{ Symlinks: true, IgnoreDanglingSymlinks: true, CopyFunction: shutil.Copy, }); err != nil { log.Fatalf("error copying rootfs to a temporary directory: %v", err) } }
// 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 }
func CopyDir(src, target string) error { return shutil.CopyTree(src, target, copyTreeOptions) }
// runDiags takes logDir and runs a sequence of commands to collect diagnostics func runDiags(logDir string) { // Note: in for the cmd field in this struct, it can't handle args quoted with space in it // For example, you can't add cmd "do this", since after the `strings.Fields` it will become `"do` and `this"` cmds := []diagCmd{ {"", "date", "date"}, {"", "hostname", "hostname"}, {"Dumping netstat", "netstat --all --numeric", "netstat"}, {"Dumping routes (IPv4)", "ip -4 route", "ipv4_route"}, {"Dumping routes (IPv6)", "ip -6 route", "ipv6_route"}, {"Dumping interface info (IPv4)", "ip -4 addr", "ipv4_addr"}, {"Dumping interface info (IPv6)", "ip -6 addr", "ipv6_addr"}, {"Dumping iptables (IPv4)", "iptables-save", "ipv4_tables"}, {"Dumping iptables (IPv6)", "ip6tables-save", "ipv6_tables"}, {"Dumping ipsets", "ipset list", "ipsets"}, {"Dumping ipsets (container)", "docker run --privileged --net=host calico/node ipset list", "ipset_container"}, {"Copying journal for calico-node.service", "journalctl -u calico-node.service --no-pager", "journalctl_calico_node"}, {"Dumping felix stats", "pkill -SIGUSR1 felix", ""}, } // Make sure the command is run with super user privileges enforceRoot() fmt.Println("Collecting diagnostics") // Create a temp directory in /tmp tmpDir, err := ioutil.TempDir("", "calico") if err != nil { fmt.Printf("Error creating temp directory to dump logs: %v\n", err) os.Exit(1) } fmt.Println("Using temp dir:", tmpDir) err = os.Chdir(tmpDir) if err != nil { fmt.Printf("Error changing directory to temp directory to dump logs: %v\n", err) os.Exit(1) } os.Mkdir("diagnostics", os.ModeDir) diagsTmpDir := filepath.Join(tmpDir, "diagnostics") for _, v := range cmds { writeDiags(v, diagsTmpDir) } tmpLogDir := filepath.Join(diagsTmpDir, "logs") // Check if the logDir provided/default exists and is a directory fileInfo, err := os.Stat(logDir) if err != nil { fmt.Printf("Error copying log files: %v\n", err) } else if fileInfo.IsDir() { fmt.Println("Copying Calico logs") err = shutil.CopyTree(logDir, tmpLogDir, nil) if err != nil { fmt.Printf("Error copying log files: %v\n", err) } } else { fmt.Printf("No logs found in %s; skipping log copying", logDir) } // Get the current time and create a tar.gz file with the timestamp in the name tarFile := fmt.Sprintf("diags-%s.tar.gz", time.Now().Format("20060102_150405")) // Have to use shell to compress the file because Go archive/tar can't handle // some header metadata longer than a certain length (Ref: https://github.com/golang/go/issues/12436) err = exec.Command("tar", "-zcvf", tarFile, "diagnostics").Run() if err != nil { fmt.Printf("Error compressing the diagnostics: %v\n", err) } tarFilePath := filepath.Join(tmpDir, tarFile) fmt.Printf("\nDiags saved to %s\n", tarFilePath) fmt.Printf(`If required, you can upload the diagnostics bundle to a file sharing service such as transfer.sh using curl or similar. For example: curl --upload-file %s https://transfer.sh/%s`, tarFilePath, tarFilePath) fmt.Println() }
func runExec(context *cli.Context) { flagIn := context.String("in") flagCmd := context.String("cmd") flagOut := context.String("out") if flagIn == "" || flagCmd == "" || flagOut == "" { log.Fatalf("--in, --cmd, and --out need to be set") } flagNoOverlay := context.Bool("no-overlay") useOverlay := util.SupportsOverlay() && !flagNoOverlay s := getStore() mounts, err := getMounts(context) if err != nil { log.Fatalf("error parsing mounts: %v", err) } // Render the given image in tree store imageHash, err := renderInStore(s, flagIn) if err != nil { log.Fatalf("error rendering image in store: %s", err) } imagePath := s.GetTreeStorePath(imageHash) // Create a tmp directory tmpDir, err := ioutil.TempDir("", "acbuild-") if err != nil { log.Fatalf("error creating temporary directory: %s", err) } // Copy the manifest into the tmp directory if err := shutil.CopyFile(filepath.Join(imagePath, aci.ManifestFile), filepath.Join(tmpDir, aci.ManifestFile), true); err != nil { log.Fatalf("error copying manifest to a temporary directory: %s", err) } // Extract a ImageManifest from the manifest file manifestFile, err := os.Open(filepath.Join(tmpDir, aci.ManifestFile)) if err != nil { log.Fatalf("error opening the copied manifest file: %v", err) } manifestContent, err := ioutil.ReadAll(manifestFile) if err != nil { log.Fatalf("error reading the copied manifest file: %v", err) } im := &schema.ImageManifest{} if err := im.UnmarshalJSON(manifestContent); err != nil { log.Fatalf("error unmarshalling JSON to manifest: %v", err) } // If an output image name is not given, we grab it from the input ACI flagImageName := context.String("output-image-name") if flagImageName == "" { flagImageName = string(im.Name) } flagJail := context.Bool("jail") // If the system supports overlayfs, use it. // Otherwise, copy the entire rendered image to a working directory. storeRootfsDir := filepath.Join(imagePath, aci.RootfsDir) tmpRootfsDir := filepath.Join(tmpDir, aci.RootfsDir) if useOverlay { upperDir, err := mountOverlayfs(tmpRootfsDir, storeRootfsDir) if err != nil { log.Fatalf("error mounting overlayfs: %v", err) } // Note that defer functions are not run if the program // exits via os.Exit() and by extension log.Fatal(), which // is the behaviour that we want. defer unmountOverlayfs(tmpRootfsDir) if err := runCmdInDir(im, flagCmd, tmpRootfsDir, flagJail, mounts); err != nil { log.Fatalf("error executing command: %v", err) } // We store the delta (i.e. side effects of the executed command) into its own ACI // The name of the ACI is a hash of (command, hash of input image). This will make // implementing caching easier in the future. deltaACIName, err := util.Hash(flagCmd, imageHash) if err != nil { log.Fatalf("error hashing (%s %s): %s", flagCmd, imageHash, err) } deltaManifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(deltaACIName), } deltaACIDir, err := util.PrepareACIDir(deltaManifest, upperDir) if err != nil { log.Fatalf("error preparing delta ACI dir: %v", err) } // Create a temp directory for placing delta ACI deltaACITempDir, err := ioutil.TempDir("", "") if err != nil { log.Fatalf("error creating temp dir to put delta ACI: %v", err) } deltaACIPath := filepath.Join(deltaACITempDir, "delta.aci") // Build the delta ACI if err := util.BuildACI(deltaACIDir, deltaACIPath, true, false); err != nil { log.Fatalf("error building delta ACI: %v", err) } // Put the delta ACI into tree store deltaACIFile, err := os.Open(deltaACIPath) if err != nil { log.Fatalf("error opening the delta ACI file: %v", err) } deltaKey, err := s.WriteACI(deltaACIFile, false) if err != nil { log.Fatalf("error writing the delta ACI into the tree store: %v", err) } deltaKeyHash, err := types.NewHash(deltaKey) if err != nil { log.Fatalf("error creating hash from an image ID (%s): %v", deltaKeyHash, err) } // The manifest for the output ACI manifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(flagImageName), } if context.Bool("split") { layers, err := util.ExtractLayers(s, flagIn) if err != nil { log.Fatalf("error extracting layers from %s: %v", flagIn, err) } manifest.Dependencies = append(manifest.Dependencies, layers...) manifest.Dependencies = append(manifest.Dependencies, types.Dependency{ ImageName: types.ACIdentifier(deltaACIName), ImageID: deltaKeyHash, }) } else { layer, err := util.ExtractLayerInfo(s, flagIn) if err != nil { log.Fatalf("error extracting layer info from input ACI: %v", err) } // two layers: // 1. The original ACI // 2. The delta ACI manifest.Dependencies = types.Dependencies{ layer, types.Dependency{ ImageName: types.ACIdentifier(deltaACIName), ImageID: deltaKeyHash, }, } } // The rootfs is empty aciDir, err := util.PrepareACIDir(manifest, "") if err != nil { log.Fatalf("error prepareing ACI dir %v: %v", aciDir, err) } // Build the output ACI if err := util.BuildACI(aciDir, flagOut, true, false); err != nil { log.Fatalf("error building the final output ACI: %v", err) } } else { if err := shutil.CopyTree(storeRootfsDir, tmpRootfsDir, &shutil.CopyTreeOptions{ Symlinks: true, IgnoreDanglingSymlinks: true, CopyFunction: shutil.Copy, }); err != nil { log.Fatalf("error copying rootfs to a temporary directory: %v", err) } if err := runCmdInDir(im, flagCmd, tmpRootfsDir, flagJail, mounts); err != nil { log.Fatalf("error executing command: %v", err) } err = util.BuildACI(tmpDir, flagOut, true, false) if err != nil { log.Fatalf("error building output ACI image: %v", err) } } }