Files
nexus-vagrant/provision/use-oci-repository.sh

164 lines
5.8 KiB
Bash

#!/bin/bash
set -euxo pipefail
nexus_domain="$(hostname --fqdn)"
registry_domain="$nexus_domain"
registry_username='alice.doe'
registry_password='password'
oci_hosted_repository_name='oci-hosted'
oci_hosted_repository_host="$registry_domain/$oci_hosted_repository_name"
oci_hosted_repository_api_url="https://$registry_domain/v2/$oci_hosted_repository_name"
# login into the registry.
echo "logging in the registry $registry_domain..."
docker login "$registry_domain" --username "$registry_username" --password-stdin <<EOF
$registry_password
EOF
mkdir -p tmp/use-oci-repository && cd tmp/use-oci-repository
#
# test the oci repository.
# see https://github.com/golang/go/tags
# renovate: datasource=github-tags depName=golang/go extractVersion=go(?<version>.+)
go_version='1.27.1'
cat >main.go <<'EOF'
package main
import (
"fmt"
"flag"
"log"
"net/http"
)
func main() {
log.SetFlags(0)
var listenAddress = flag.String("listen", ":8000", "Listen address.")
flag.Parse()
if flag.NArg() != 0 {
flag.Usage()
log.Fatalf("\nERROR You MUST NOT pass any positional arguments")
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Printf("%s %s%s\n", r.Method, r.Host, r.URL)
fmt.Fprintf(w, "%s %s%s\n", r.Method, r.Host, r.URL)
})
fmt.Printf("Listening at http://%s\n", *listenAddress)
err := http.ListenAndServe(*listenAddress, nil)
if err != nil {
log.Fatalf("Failed to ListenAndServe: %v", err)
}
}
EOF
cat >go.mod <<EOF
module example.com/go-hello-oci
go $go_version
EOF
cat >Dockerfile <<EOF
FROM golang:$go_version-trixie AS builder
WORKDIR /app
COPY go.* main.go ./
RUN CGO_ENABLED=0 go build -ldflags="-s"
# NB we use the trixie-slim (instead of scratch) image so we
# can enter the container to execute bash etc.
FROM debian:trixie-slim
COPY --from=builder /app/go-hello-oci .
WORKDIR /
EXPOSE 8000
ENTRYPOINT ["/go-hello-oci"]
EOF
# build the image.
docker build -t go-hello-oci:1.0.0 .
docker image ls go-hello-oci:1.0.0
# push the image to the oci-hosted repository.
docker tag go-hello-oci:1.0.0 "$oci_hosted_repository_host/go-hello-oci:1.0.0"
docker push "$oci_hosted_repository_host/go-hello-oci:1.0.0"
# build the image sbom.
syft \
scan \
"docker:$oci_hosted_repository_host/go-hello-oci:1.0.0" \
--output spdx-json=go-hello-oci-1.0.0.sbom.spdx.json \
--output cyclonedx-json=go-hello-oci-1.0.0.sbom.cyclonedx.json
# attach the sbom to the image in the oci-hosted repository.
oras attach \
"$oci_hosted_repository_host/go-hello-oci:1.0.0" \
--artifact-type application/spdx+json \
go-hello-oci-1.0.0.sbom.spdx.json
oras attach \
"$oci_hosted_repository_host/go-hello-oci:1.0.0" \
--artifact-type application/vnd.cyclonedx+json \
go-hello-oci-1.0.0.sbom.cyclonedx.json
# show the repository (image) details directly from the oci-hosted repository api.
# see https://specs.opencontainers.org/distribution-spec/?v=v1.1.1
# see https://github.com/opencontainers/distribution-spec
wget -qO- --user "$registry_username" --password "$registry_password" \
"$oci_hosted_repository_api_url/go-hello-oci/tags/list" | jq .
oci_image_index="$(wget -qO- --user "$registry_username" --password "$registry_password" \
'--header=Accept: application/vnd.oci.image.index.v1+json' \
"$oci_hosted_repository_api_url/go-hello-oci/manifests/1.0.0")"
echo "$oci_image_index" | jq .
oci_image_manifest_digest="$(echo "$oci_image_index" | jq -r .manifests[0].digest)"
oci_image_manifest="$(wget -qO- --user "$registry_username" --password "$registry_password" \
'--header=Accept: application/vnd.oci.image.manifest.v1+json' \
"$oci_hosted_repository_api_url/go-hello-oci/manifests/$oci_image_manifest_digest")"
echo "$oci_image_manifest" | jq .
oci_image_config_digest="$(echo "$oci_image_manifest" | jq -r .config.digest)"
config_digest="$(echo "$oci_image_manifest" | jq -r .config.digest)"
wget -qO- --user "$registry_username" --password "$registry_password" \
"$oci_hosted_repository_api_url/go-hello-oci/blobs/$oci_image_config_digest" | jq .
# show the repository (image) details using oras.
oras repo tags "$oci_hosted_repository_host/go-hello-oci"
oci_image_index="$(oras manifest fetch "$oci_hosted_repository_host/go-hello-oci:1.0.0")"
echo "$oci_image_index" | jq .
oci_image_manifest_digest="$(echo "$oci_image_index" | jq -r .manifests[0].digest)"
oras manifest fetch "$oci_hosted_repository_host/go-hello-oci@$oci_image_manifest_digest" | jq .
oras manifest fetch-config "$oci_hosted_repository_host/go-hello-oci@$oci_image_manifest_digest" | jq .
# show the image referrers (aka references; aka associations; aka attachments;
# aka artifacts; aka attestations) using oras.
oras discover "$oci_hosted_repository_host/go-hello-oci:1.0.0"
oras discover "$oci_hosted_repository_host/go-hello-oci:1.0.0" --format json | jq .
# dump the first lines of the referenced image sbom.
# NB the sbom is wrapped in a image artifact.
sbom_image_manifest_digest="$(oras discover \
"$oci_hosted_repository_host/go-hello-oci:1.0.0" \
--artifact-type application/vnd.cyclonedx+json \
--format json \
| jq -r '.referrers[0].digest')"
sbom_image_manifest="$(oras manifest fetch \
"$oci_hosted_repository_host/go-hello-oci@$sbom_image_manifest_digest" \
--format json)"
sbom_blob_digest="$(echo "$sbom_image_manifest" | jq -r '.content.layers[0].digest')"
oras blob fetch \
"$oci_hosted_repository_host/go-hello-oci@$sbom_blob_digest" \
--output - \
| jq . \
| head -n 10 \
|| true
# remove it from local cache.
docker image remove go-hello-oci:1.0.0
docker image remove "$oci_hosted_repository_host/go-hello-oci:1.0.0"
# pull it from the oci-hosted repository.
docker pull "$oci_hosted_repository_host/go-hello-oci:1.0.0"