Skip to content

Commit

Permalink
Add imgref module that parses ostree-container image references
Browse files Browse the repository at this point in the history
This is needed in the MCO, see openshift/machine-config-operator#3857
  • Loading branch information
cgwalters committed Sep 13, 2023
1 parent 4a5f57b commit f0aecc4
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 0 deletions.
10 changes: 10 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

"github.com/containers/image/v5/docker/reference"
yaml "gopkg.in/yaml.v3"

"github.com/coreos/rpmostree-client-go/pkg/imgref"
)

// Status summarizes the current worldview of the rpm-ostree daemon.
Expand Down Expand Up @@ -172,6 +174,14 @@ func (s *Deployment) GetBaseChecksum() string {
return s.Checksum
}

// Parse the deployment's container image reference.
func (d *Deployment) RequireContainerImage() (*imgref.OstreeImageReference, error) {
if d.ContainerImageReference == "" {
return nil, fmt.Errorf("deployment is not using a container origin")
}
return imgref.Parse(d.ContainerImageReference)
}

// Remove the pending deployment.
func (client *Client) RemovePendingDeployment() error {
return client.run("cleanup", "-p")
Expand Down
4 changes: 4 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ func TestParseFcosContainer(t *testing.T) {

firstDeploy := s.Deployments[0]
assert.Equal(t, firstDeploy.ContainerImageReference, "ostree-unverified-registry:quay.io/fedora/fedora-coreos:testing-devel")

ir, err := firstDeploy.RequireContainerImage()
assert.Nil(t, err)
assert.Equal(t, ir.Imgref.Image, "quay.io/fedora/fedora-coreos:testing-devel")
}

func TestParseFcosWithOverrides(t *testing.T) {
Expand Down
91 changes: 91 additions & 0 deletions pkg/imgref/imgref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// package imgref parses ostree-container image references.
package imgref

import (
"fmt"
"strings"
)

// SignatureVerify is a mirror of https://docs.rs/ostree-ext/latest/ostree_ext/container/enum.SignatureSource.html
type SignatureVerify struct {
AllowInsecure bool
OstreeRemote string
}

type ImageReferenceWithTransport struct {
// IsRegistry is true if this image is fetched from a registry
Transport string
// Image is the unparsed string representation of a container image.
// For e.g. oci-archive: it will be a filesystem path.
// It can include a tag or digest (or not).
Image string
}

// OstreeImage reference captures an ostree signature verification policy alongside an image reference.
// This mirrors https://docs.rs/ostree-ext/latest/ostree_ext/container/struct.OstreeImageReference.html
type OstreeImageReference struct {
Sigverify SignatureVerify
Imgref ImageReferenceWithTransport
}

// IsRegistry returns true if this image will be fetched from a registry.
func (ir *ImageReferenceWithTransport) IsRegistry() bool {
return ir.Transport == "registry"
}

// parseImageReference mirrors https://docs.rs/ostree-ext/0.12.0/src/ostree_ext/container/mod.rs.html#129
func parseImageReference(ir string) (*ImageReferenceWithTransport, error) {
irparts := strings.SplitN(ir, ":", 2)
if len(irparts) < 2 {
return nil, fmt.Errorf("invalid image reference (missing ':'): %s", ir)
}

imgref := ImageReferenceWithTransport{
Transport: irparts[0],
Image: irparts[1],
}
// docker:// is a special case; we want to rename it and also trim the //
if imgref.Transport == "docker" {
imgref.Transport = "registry"
if !strings.HasPrefix(imgref.Image, "//") {
return nil, fmt.Errorf("missing // in docker://")
}
imgref.Image = imgref.Image[2:]
}
return &imgref, nil
}

func Parse(ir string) (*OstreeImageReference, error) {
parts := strings.SplitN(ir, ":", 2)
if len(parts) != 2 {
panic("Expected 2 parts")
}
first := parts[0]
second := parts[1]
sigverify := SignatureVerify{}
rest := second
switch first {
case "ostree-image-signed":
case "ostree-unverified-image":
sigverify = SignatureVerify{AllowInsecure: true}
case "ostree-unverified-registry":
sigverify = SignatureVerify{AllowInsecure: true}
rest = "registry:" + second
case "ostree-remote-registry":
subparts := strings.SplitN(rest, ":", 2)
sigverify = SignatureVerify{OstreeRemote: subparts[0], AllowInsecure: true}
rest = "registry:" + subparts[1]
case "ostree-remote-image":
subparts := strings.SplitN(rest, ":", 2)
sigverify = SignatureVerify{OstreeRemote: subparts[0], AllowInsecure: true}
rest = subparts[1]
default:
return nil, fmt.Errorf("invalid ostree image reference (unmatched scheme): %s", ir)
}

imgref, err := parseImageReference(rest)
if err != nil {
return nil, err
}
return &OstreeImageReference{Sigverify: sigverify, Imgref: *imgref}, nil
}
37 changes: 37 additions & 0 deletions pkg/imgref/imgref_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package imgref

import (
_ "embed"
"testing"

"github.com/stretchr/testify/assert"
)

func TestOstreeImageReference(t *testing.T) {
equivalent := []string{
"ostree-unverified-image:docker://quay.io/exampleos/blah",
"ostree-unverified-registry:quay.io/exampleos/blah",
}
for _, ir := range equivalent {
ir, err := Parse(ir)
assert.Nil(t, err)
assert.True(t, ir.Sigverify.AllowInsecure)
assert.Len(t, ir.Sigverify.OstreeRemote, 0)
assert.Equal(t, ir.Imgref.Transport, "registry")
assert.Equal(t, ir.Imgref.Image, "quay.io/exampleos/blah")
}

equivalent = []string{
"ostree-remote-registry:fedora:quay.io/fedora/fedora-coreos:stable",
"ostree-remote-image:fedora:registry:quay.io/fedora/fedora-coreos:stable",
"ostree-remote-image:fedora:docker://quay.io/fedora/fedora-coreos:stable",
}
for _, ir := range equivalent {
ir, err := Parse(ir)
assert.Nil(t, err)
assert.True(t, ir.Sigverify.AllowInsecure)
assert.Equal(t, ir.Sigverify.OstreeRemote, "fedora")
assert.Equal(t, ir.Imgref.Transport, "registry")
assert.Equal(t, ir.Imgref.Image, "quay.io/fedora/fedora-coreos:stable")
}
}

0 comments on commit f0aecc4

Please sign in to comment.