commands

package
v0.0.0-...-d25a2ea Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2024 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CreateCommand = cli.Command{
	Name:      "create",
	Usage:     "create SOCI index",
	ArgsUsage: "[flags] <image_ref>",
	Flags: append(
		internal.PlatformFlags,
		cli.Int64Flag{
			Name:  spanSizeFlag,
			Usage: "Span size that soci index uses to segment layer data. Default is 4 MiB",
			Value: 1 << 22,
		},
		cli.Int64Flag{
			Name:  minLayerSizeFlag,
			Usage: "Minimum layer size to build zTOC for. Smaller layers won't have zTOC and not lazy pulled. Default is 10 MiB.",
			Value: 10 << 20,
		},
		cli.BoolTFlag{
			Name:  disableXAttrsFlag,
			Usage: "When true, adds DisableXAttrs annotation to SOCI index. This annotation often helps performance at pull time. Default is true",
		},
	),
	Action: func(cliContext *cli.Context) error {
		srcRef := cliContext.Args().Get(0)
		if srcRef == "" {
			return errors.New("source image needs to be specified")
		}

		client, ctx, cancel, err := internal.NewClient(cliContext)
		if err != nil {
			return err
		}
		defer cancel()

		cs := client.ContentStore()
		is := client.ImageService()
		srcImg, err := is.Get(ctx, srcRef)
		if err != nil {
			return err
		}
		spanSize := cliContext.Int64(spanSizeFlag)
		minLayerSize := cliContext.Int64(minLayerSizeFlag)

		if _, err := os.Stat(config.SociSnapshotterRootPath); os.IsNotExist(err) {
			if err = os.Mkdir(config.SociSnapshotterRootPath, 0711); err != nil {
				return err
			}
		} else if err != nil {
			return err
		}

		ctx, blobStore, err := store.NewContentStore(ctx, internal.ContentStoreOptions(cliContext)...)
		if err != nil {
			return err
		}

		ps, err := internal.GetPlatforms(ctx, cliContext, srcImg, cs)
		if err != nil {
			return err
		}

		artifactsDb, err := soci.NewDB(soci.ArtifactsDbPath())
		if err != nil {
			return err
		}

		builderOpts := []soci.BuildOption{
			soci.WithMinLayerSize(minLayerSize),
			soci.WithSpanSize(spanSize),
			soci.WithBuildToolIdentifier(buildToolIdentifier),
		}

		if !cliContext.Bool(disableXAttrsFlag) {
			builderOpts = append(builderOpts, soci.WithNoDisableXAttrs())
		}

		for _, plat := range ps {
			builder, err := soci.NewIndexBuilder(cs, blobStore, artifactsDb, append(builderOpts, soci.WithPlatform(plat))...)

			if err != nil {
				return err
			}

			sociIndexWithMetadata, err := builder.Build(ctx, srcImg)
			if err != nil {
				return err
			}

			err = soci.WriteSociIndex(ctx, sociIndexWithMetadata, blobStore, builder.ArtifactsDb)
			if err != nil {
				return err
			}
		}

		return nil
	},
}

CreateCommand creates SOCI index for an image Output of this command is SOCI layers and SOCI index stored in a local directory SOCI layer is named as <image-layer-digest>.soci.layer SOCI index is named as <image-manifest-digest>.soci.index

View Source
var PushCommand = cli.Command{
	Name:      "push",
	Usage:     "push SOCI artifacts to a registry",
	ArgsUsage: "[flags] <ref>",
	Description: `Push SOCI artifacts to a registry by image reference.
If multiple soci indices exist for the given image, the most recent one will be pushed.

After pushing the soci artifacts, they should be available in the registry. Soci artifacts will be pushed only
if they are available in the snapshotter's local content store.
`,
	Flags: append(append(append(
		internal.RegistryFlags,
		internal.SnapshotterFlags...),
		internal.PlatformFlags...),
		internal.ExistingIndexFlag,
		cli.Uint64Flag{
			Name:  maxConcurrentUploadsFlag,
			Usage: fmt.Sprintf("Max concurrent uploads. Default is %d", defaultMaxConcurrentUploads),
			Value: defaultMaxConcurrentUploads,
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "quiet mode",
		},
	),
	Action: func(cliContext *cli.Context) error {
		ref := cliContext.Args().First()
		quiet := cliContext.Bool("quiet")
		if ref == "" {
			return fmt.Errorf("please provide an image reference to push")
		}

		client, ctx, cancel, err := internal.NewClient(cliContext)
		if err != nil {
			return err
		}
		defer cancel()

		cs := client.ContentStore()
		is := client.ImageService()
		img, err := is.Get(ctx, ref)
		if err != nil {
			return err
		}

		ps, err := internal.GetPlatforms(ctx, cliContext, img, cs)
		if err != nil {
			return err
		}

		artifactsDb, err := soci.NewDB(soci.ArtifactsDbPath())
		if err != nil {
			return err
		}

		refspec, err := reference.Parse(ref)
		if err != nil {
			return err
		}

		dst, err := remote.NewRepository(refspec.Locator)
		if err != nil {
			return err
		}
		authClient := auth.DefaultClient

		var username string
		var secret string
		if cliContext.IsSet("user") {
			username = cliContext.String("user")
			if i := strings.IndexByte(username, ':'); i > 0 {
				secret = username[i+1:]
				username = username[0:i]
			}
		} else {
			cf := dockercliconfig.LoadDefaultConfigFile(io.Discard)
			if cf.ContainsAuth() {
				if ac, err := cf.GetAuthConfig(refspec.Hostname()); err == nil {
					username = ac.Username
					secret = ac.Password
				}
			}
		}

		authClient.Credential = func(_ context.Context, host string) (auth.Credential, error) {
			return auth.Credential{
				Username: username,
				Password: secret,
			}, nil
		}

		ctx, src, err := store.NewContentStore(ctx, internal.ContentStoreOptions(cliContext)...)
		if err != nil {
			return fmt.Errorf("cannot create local content store: %w", err)
		}

		dst.Client = authClient
		dst.PlainHTTP = cliContext.Bool("plain-http")

		debug := cliContext.GlobalBool("debug")
		if debug {
			dst.Client = &debugClient{client: authClient}
		} else {
			dst.Client = authClient
		}
		existingIndexOption := cliContext.String(internal.ExistingIndexFlagName)
		if !internal.SupportedArg(existingIndexOption, internal.SupportedExistingIndexOptions) {
			return fmt.Errorf("unexpected value for flag %s: %s, expected types %v",
				internal.ExistingIndexFlagName, existingIndexOption, internal.SupportedExistingIndexOptions)
		}

		options := oraslib.DefaultCopyGraphOptions
		if value := cliContext.Uint64(maxConcurrentUploadsFlag); value == 0 {
			options.Concurrency = defaultMaxConcurrentUploads
		} else if value > math.MaxInt {
			if !quiet {
				fmt.Printf("warning: overflow for setting --%s=%d; defaulting to %d", maxConcurrentUploadsFlag, value, defaultMaxConcurrentUploads)
			}
			options.Concurrency = defaultMaxConcurrentUploads
		} else {
			options.Concurrency = int(value)
		}
		options.PreCopy = func(_ context.Context, desc ocispec.Descriptor) error {
			if !quiet {
				fmt.Printf("pushing artifact with digest: %v\n", desc.Digest)
			}
			return nil
		}
		options.PostCopy = func(_ context.Context, desc ocispec.Descriptor) error {
			if !quiet {
				fmt.Printf("successfully pushed artifact with digest: %v\n", desc.Digest)
			}
			return nil
		}
		options.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error {
			if !quiet {
				fmt.Printf("skipped artifact with digest: %v\n", desc.Digest)
			}
			return nil
		}

		for _, platform := range ps {
			indexDescriptors, imgManifestDesc, err := soci.GetIndexDescriptorCollection(ctx, cs, artifactsDb, img, []ocispec.Platform{platform})
			if err != nil {
				return err
			}

			if len(indexDescriptors) == 0 {
				return fmt.Errorf("could not find any soci indices to push")
			}

			sort.Slice(indexDescriptors, func(i, j int) bool {
				return indexDescriptors[i].CreatedAt.Before(indexDescriptors[j].CreatedAt)
			})
			indexDesc := indexDescriptors[len(indexDescriptors)-1]

			if existingIndexOption != internal.Allow {
				if !quiet {
					fmt.Println("checking if a soci index already exists in remote repository...")
				}
				client := fs.NewOCIArtifactClient(dst)
				referrers, err := client.AllReferrers(ctx, ocispec.Descriptor{Digest: imgManifestDesc.Digest})
				if err != nil && !errors.Is(err, fs.ErrNoReferrers) {
					return fmt.Errorf("failed to fetch list of referrers: %w", err)
				}
				if len(referrers) > 0 {
					var foundMessage string
					if len(referrers) > 1 {
						foundMessage = "multiple soci indices found in remote repository"
					} else {
						foundMessage = fmt.Sprintf("soci index found in remote repository with digest: %s", referrers[0].Digest.String())
					}
					switch existingIndexOption {
					case internal.Skip:
						if !quiet {
							fmt.Printf("%s: skipping pushing artifacts for image manifest: %s\n", foundMessage, imgManifestDesc.Digest.String())
						}
						continue
					case internal.Warn:
						fmt.Printf("[WARN] %s: pushing index anyway\n", foundMessage)

					}
				}

			}

			if quiet {
				fmt.Println(indexDesc.Digest.String())
			} else {
				fmt.Printf("pushing soci index with digest: %v\n", indexDesc.Digest)
			}

			err = oraslib.CopyGraph(context.Background(), src, dst, indexDesc.Descriptor, options)
			if err != nil {
				return fmt.Errorf("error pushing graph to remote: %w", err)
			}

		}
		return nil
	},
}

PushCommand is a command to push an image artifacts from local content store to the remote repository

View Source
var RebuildDBCommand = cli.Command{
	Name:  "rebuild-db",
	Usage: `rebuild the artifacts database. You should use this command after "rpull" so that indices/ztocs can be discovered by commands like "soci index list", and after "index rm" when using the containerd content store so that deleted orphaned zTOCs will be forgotten`,
	Action: func(cliContext *cli.Context) error {
		client, ctx, cancel, err := internal.NewClient(cliContext)
		if err != nil {
			return err
		}
		defer cancel()
		containerdContentStore := client.ContentStore()
		artifactsDb, err := soci.NewDB(soci.ArtifactsDbPath())
		if err != nil {
			return err
		}
		ctx, blobStore, err := store.NewContentStore(ctx, internal.ContentStoreOptions(cliContext)...)
		if err != nil {
			return err
		}

		contentStorePath, err := store.GetContentStorePath(store.ContentStoreType(cliContext.GlobalString("content-store")))
		if err != nil {
			return err
		}

		blobStorePath := filepath.Join(contentStorePath, "blobs")
		return artifactsDb.SyncWithLocalStore(ctx, blobStore, blobStorePath, containerdContentStore)
	},
}

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL