Пример #1
0
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)
	}
}
Пример #2
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
}
Пример #3
0
func CopyDir(src, target string) error {
	return shutil.CopyTree(src, target, copyTreeOptions)
}
Пример #4
0
// 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()

}
Пример #5
0
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)
		}
	}
}