Skip to content

Instantly share code, notes, and snippets.

@everettraven
Last active September 3, 2024 22:59
Show Gist options
  • Save everettraven/4691c8cc6181f28b129f46cceeb602b5 to your computer and use it in GitHub Desktop.
Save everettraven/4691c8cc6181f28b129f46cceeb602b5 to your computer and use it in GitHub Desktop.
Channel name validation test
package main
import (
"archive/tar"
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/containerd/containerd/archive"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/operator-framework/operator-registry/alpha/declcfg"
)
const ConfigDirLabel = "operators.operatorframework.io.index.configs.v1"
func main() {
catalogImages := []string{
"registry.redhat.io/redhat/certified-operator-index:v4.12",
"registry.redhat.io/redhat/certified-operator-index:v4.13",
"registry.redhat.io/redhat/certified-operator-index:v4.14",
"registry.redhat.io/redhat/certified-operator-index:v4.15",
"registry.redhat.io/redhat/certified-operator-index:v4.16",
"registry.redhat.io/redhat/certified-operator-index:v4.17",
"registry.redhat.io/redhat/redhat-operator-index:v4.12",
"registry.redhat.io/redhat/redhat-operator-index:v4.13",
"registry.redhat.io/redhat/redhat-operator-index:v4.14",
"registry.redhat.io/redhat/redhat-operator-index:v4.15",
"registry.redhat.io/redhat/redhat-operator-index:v4.16",
"registry.redhat.io/redhat/redhat-operator-index:v4.17",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.12",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.13",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.14",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.15",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.16",
"registry.redhat.io/redhat/redhat-marketplace-index:v4.17",
"registry.redhat.io/redhat/community-operator-index:v4.12",
"registry.redhat.io/redhat/community-operator-index:v4.13",
"registry.redhat.io/redhat/community-operator-index:v4.14",
"registry.redhat.io/redhat/community-operator-index:v4.15",
"registry.redhat.io/redhat/community-operator-index:v4.16",
"registry.redhat.io/redhat/community-operator-index:v4.17",
"quay.io/operatorhubio/catalog:latest",
}
for _, catalogRef := range catalogImages {
unpackPath, err := os.MkdirTemp(".", "unpack")
if err != nil {
log.Fatalf("error creating temp directory: %w", err)
}
imgRef, err := name.ParseReference(catalogRef)
if err != nil {
log.Fatalf("error parsing name reference %q: %w", catalogRef, err)
}
img, err := remote.Image(imgRef, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
log.Fatalf("error fetching remote image %q: %w", imgRef.Name(), err)
}
cfgFile, err := img.ConfigFile()
if err != nil {
log.Fatalf("error parsing remote image %q config file: %w", imgRef.Name(), err)
}
dirToUnpack, ok := cfgFile.Config.Labels[ConfigDirLabel]
if !ok {
log.Fatalf("catalog image %q is missing the required label %q", imgRef.String(), ConfigDirLabel)
}
layers, err := img.Layers()
if err != nil {
log.Fatalf("error getting image layers: %w", err)
}
dirBase := filepath.Base(dirToUnpack)
for _, layer := range layers {
layerRc, err := layer.Uncompressed()
if err != nil {
log.Fatalf("error getting uncompressed layer data: %w", err)
}
// Apply the layer contents, but filter on the directory that contains catalog contents so we only cache the
// catalog contents and nothing else. This filter ensures that the files created have the proper UID and GID
// for the filesystem they will be stored on to ensure no permission errors occur when attempting to create the
// files.
_, err = archive.Apply(context.TODO(), unpackPath, layerRc, archive.WithFilter(func(th *tar.Header) (bool, error) {
th.Uid = os.Getuid()
th.Gid = os.Getgid()
dir, file := filepath.Split(th.Name)
return (dir == "" && file == dirBase) || strings.HasPrefix(dir, fmt.Sprintf("%s/", dirBase)), nil
}))
if err != nil {
log.Fatalf("error applying layer to archive: %w", err)
}
}
catalogFS := os.DirFS(unpackPath)
walkErrs := []error{}
_ = declcfg.WalkMetasFS(context.TODO(), catalogFS, func(path string, meta *declcfg.Meta, err error) error {
if len(meta.Name) > 253 {
walkErrs = append(walkErrs, fmt.Errorf("catalog %q has schema %q with incompatible name %q: name is > 253 characters", catalogRef, meta.Schema, meta.Name))
}
channelRegex := regexp.MustCompile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$")
match := channelRegex.MatchString(meta.Name)
if !match {
walkErrs = append(walkErrs, fmt.Errorf("catalog %q has schema %q with incompatible name %q: name does not match regex", catalogRef, meta.Schema, meta.Name))
}
return nil
})
if len(walkErrs) > 0 {
fmt.Println("#", catalogRef)
fmt.Println(errors.Join(walkErrs...))
}
os.RemoveAll(unpackPath)
}
}
# registry.redhat.io/redhat/certified-operator-index:v4.12
catalog "registry.redhat.io/redhat/certified-operator-index:v4.12" has schema "olm.channel" with incompatible name "--default-channel": name does not match regex
# registry.redhat.io/redhat/certified-operator-index:v4.13
catalog "registry.redhat.io/redhat/certified-operator-index:v4.13" has schema "olm.channel" with incompatible name "--default-channel": name does not match regex
# registry.redhat.io/redhat/certified-operator-index:v4.14
catalog "registry.redhat.io/redhat/certified-operator-index:v4.14" has schema "olm.channel" with incompatible name "--default-channel": name does not match regex
# registry.redhat.io/redhat/certified-operator-index:v4.15
catalog "registry.redhat.io/redhat/certified-operator-index:v4.15" has schema "olm.channel" with incompatible name "--default-channel": name does not match regex
# registry.redhat.io/redhat/certified-operator-index:v4.16
catalog "registry.redhat.io/redhat/certified-operator-index:v4.16" has schema "olm.channel" with incompatible name "--default-channel": name does not match regex
# quay.io/operatorhubio/catalog:latest
catalog "quay.io/operatorhubio/catalog:latest" has schema "olm.channel" with incompatible name "original_40": name does not match regex
catalog "quay.io/operatorhubio/catalog:latest" has schema "olm.channel" with incompatible name "original_41": name does not match regex
catalog "quay.io/operatorhubio/catalog:latest" has schema "olm.channel" with incompatible name "original_42": name does not match regex
catalog "quay.io/operatorhubio/catalog:latest" has schema "olm.channel" with incompatible name "original_43": name does not match regex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment