regclient / regclient

Docker and OCI Registry Client in Go and tooling using those libraries.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] getting image labels via library/API

tcurdt opened this issue · comments

Question

I am trying to use regclient as library from my own code base.
I am trying to implement the same as regctl image inspect ghcr.io/aquasecurity/trivy:latest and then extract the labels.

I've looked at the tests and the cmd implementation and so far came up with:

	getRef, err := ref.New("ghcr.io/aquasecurity/trivy:latest")
	if err != nil {
		return fmt.Errorf("Failed creating getRef: %v", err)
	}

	mGet, err := rc.ManifestGet(ctx, getRef)
	if err != nil {
		return fmt.Errorf("Failed running ManifestGet: %v", err)
	}

	if manifest.GetMediaType(mGet) != types.MediaTypeDocker2Manifest {
		// Unexpected media type: application/vnd.docker.distribution.manifest.list.v2+json
		// return fmt.Errorf("Unexpected media type: %s", manifest.GetMediaType(mGet))
	}

	fmt.Printf("isList=%@\n", mGet.IsList())

	plat := platform.Local()
	desc, err := manifest.GetPlatformDesc(mGet, &plat)
	if err != nil {
		fmt.Printf("%@\n", err)
	}

	m, err := rc.ManifestGet(ctx, getRef, regclient.WithManifestDesc(*desc))

	body, _ := m.RawBody()
	fmt.Printf("m=%@\n", string(body))
	if err != nil {
		fmt.Printf("%@\n", err)
	}

How to get to the labels?

It seems like the first call gets a list?
And the platform is used as a filter?
But how do I get from the manifest to the labels? The body looks just like this:

m=%!@(string={
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
      "mediaType": "application/vnd.docker.container.image.v1+json",
      "size": 2000,
      "digest": "sha256:529604a42cb643b098561eb59dda41d2fe3b29913570b2f6a3a1d3ab3d77af10"
   },
   "layers": [
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 3333033,
         "digest": "sha256:2c03dbb20264f09924f9eab176da44e5421e74a78b09531d3c63448a7baa7c59"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 5291926,
         "digest": "sha256:51824a6a03669e32e86dcbd1ebfbf9e579efb981c14c0b6f7c0b991e7471ac71"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 50297857,
         "digest": "sha256:4571becd293d30590e1330e7206f8c6c1e863fb359b579178ebed9ae7708bd95"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 4234,
         "digest": "sha256:c47530373cb2bc40aa641c7bc66434f4bbb33b3a4dccba81fd652229c117fa7f"
      }
   ]
})

How to pass credentials?

Also not sure yet how to provide registry credentials. (Hence the public image)

I would appreciate some pointers nudging me in the right direction.

Version

github.com/regclient/regclient v0.5.6

Environment

Currently running on macOS on arm

Anything else

Labels are part of the image config.

To go from the manifest the config, you need to convert it to an image manifest, get the config blob, and convert that to an OCI config structure. That's done in regctl at:

regclient/cmd/regctl/image.go

Lines 1213 to 1225 in a25d343

mi, ok := m.(manifest.Imager)
if !ok {
return fmt.Errorf("manifest does not support image methods%.0w", types.ErrUnsupportedMediaType)
}
cd, err := mi.GetConfig()
if err != nil {
return err
}
blobConfig, err := rc.BlobGetOCIConfig(ctx, r, cd)
if err != nil {
return err
}

For auth, you would create the rc with the WithConfigHost option:
https://pkg.go.dev/github.com/regclient/regclient#WithConfigHost

The config.Host would need a Name, User, and Pass defined (unless using a credential helper):
https://pkg.go.dev/github.com/regclient/regclient@v0.5.6/config#Host

If the registry is logged in using Docker, you can import the credentials from that using WithDockerCreds:
https://pkg.go.dev/github.com/regclient/regclient#WithDockerCreds

I'm going to leave this open for a bit because documentation in the Go Docs could use more details and examples to make this easier to figure out.

Thanks! Seems like I got it working!

So this is the correct way? or did I miss something?
(I stripped the error handling for the overview)

	ctx := context.Background()

	rc := regclient.New(
		regclient.WithConfigHost(hosts...),
	)

	r, err := ref.New("ghcr.io/foo/bar:latest")

	m, err := rc.ManifestGet(ctx, r)

	plat := platform.Local()
	desc, err := manifest.GetPlatformDesc(m, &plat)

	m, err = rc.ManifestGet(ctx, r, regclient.WithManifestDesc(*desc))

	mi, ok := m.(manifest.Imager)

	cd, err := mi.GetConfig()

	blobConfig, err := rc.BlobGetOCIConfig(ctx, r, cd)

	body, err := blobConfig.RawBody()

	var imageConfig OCIConfig
	err = yaml.Unmarshal(body, &imageConfig)

	fmt.Println(imageConfig.Config.Labels["org.opencontainers.image.revision"])

rc.BlobGetOCIConfig already unmarshals the blob for you, so you can call GetConfig to get access to the data structure directly:

https://pkg.go.dev/github.com/regclient/regclient@v0.5.6/types/blob#BOCIConfig.GetConfig

I kind of suspected I something like that :)
Thanks for the help!
Works!