Merge pull request #17624 from containers/dependabot/go_modules/github.com/coreos/stream-metadata-go-0.4.1

build(deps): bump github.com/coreos/stream-metadata-go from 0.0.0-20210225230131-70edb9eb47b3 to 0.4.1
This commit is contained in:
OpenShift Merge Robot 2023-02-27 17:55:56 +01:00 committed by GitHub
commit 6d2cd36fe6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 387 additions and 46 deletions

2
go.mod
View file

@ -19,7 +19,7 @@ require (
github.com/containers/psgo v1.8.0
github.com/containers/storage v1.45.4
github.com/coreos/go-systemd/v22 v22.5.0
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/coreos/stream-metadata-go v0.4.1
github.com/cyphar/filepath-securejoin v0.2.3
github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001
github.com/docker/docker v23.0.1+incompatible

4
go.sum
View file

@ -308,8 +308,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 h1:0JspqV66RwYqYfvi8lCUoL5zUZMh9uN4hx/J5+NRXIE=
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3/go.mod h1:RTjQyHgO/G37oJ3qnqYK6Z4TPZ5EsaabOtfMjVXmgko=
github.com/coreos/stream-metadata-go v0.4.1 h1:5fCXpH/cDi8cR2hEpkc5dS8+Kp23tZeuKfSnRql5VOI=
github.com/coreos/stream-metadata-go v0.4.1/go.mod h1:Lwjwqf1zwnVa7uy/v/KW28eUkda4jnOTOW8sx9s4kzc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=

View file

@ -4,10 +4,14 @@
package fedoracoreos
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"github.com/coreos/stream-metadata-go/fedoracoreos/internals"
"github.com/coreos/stream-metadata-go/stream"
)
const (
@ -25,3 +29,32 @@ func GetStreamURL(stream string) url.URL {
u.Path = fmt.Sprintf("streams/%s.json", stream)
return u
}
func getStream(u url.URL) (*stream.Stream, error) {
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
var s stream.Stream
err = json.Unmarshal(body, &s)
if err != nil {
return nil, err
}
return &s, nil
}
// FetchStream fetches and parses stream metadata for a stream
func FetchStream(streamName string) (*stream.Stream, error) {
u := GetStreamURL(streamName)
s, err := getStream(u)
if err != nil {
return nil, fmt.Errorf("failed to fetch stream %s: %w", streamName, err)
}
return s, nil
}

View file

@ -9,7 +9,7 @@ import (
)
// Index models the release index:
// https://github.com/coreos/fedora-coreos-tracker/tree/master/metadata/release-index
// https://github.com/coreos/fedora-coreos-tracker/tree/main/metadata/release-index
type Index struct {
Note string `json:"note"` // used to note to users not to consume the release metadata index
Releases []IndexRelease `json:"releases"`
@ -52,18 +52,24 @@ type Arch struct {
// Media contains release details for various platforms
type Media struct {
Aliyun *PlatformBase `json:"aliyun"`
Aws *PlatformAws `json:"aws"`
Azure *PlatformBase `json:"azure"`
Digitalocean *PlatformBase `json:"digitalocean"`
Exoscale *PlatformBase `json:"exoscale"`
Gcp *PlatformGcp `json:"gcp"`
Ibmcloud *PlatformBase `json:"ibmcloud"`
Metal *PlatformBase `json:"metal"`
Openstack *PlatformBase `json:"openstack"`
Qemu *PlatformBase `json:"qemu"`
Vmware *PlatformBase `json:"vmware"`
Vultr *PlatformBase `json:"vultr"`
Aliyun *PlatformAliyun `json:"aliyun"`
Aws *PlatformAws `json:"aws"`
Azure *PlatformBase `json:"azure"`
AzureStack *PlatformBase `json:"azurestack"`
Digitalocean *PlatformBase `json:"digitalocean"`
Exoscale *PlatformBase `json:"exoscale"`
Gcp *PlatformGcp `json:"gcp"`
Ibmcloud *PlatformIBMCloud `json:"ibmcloud"`
KubeVirt *PlatformKubeVirt `json:"kubevirt"`
Metal *PlatformBase `json:"metal"`
Nutanix *PlatformBase `json:"nutanix"`
Openstack *PlatformBase `json:"openstack"`
PowerVS *PlatformIBMCloud `json:"powervs"`
Qemu *PlatformBase `json:"qemu"`
QemuSecex *PlatformBase `json:"qemu-secex"`
VirtualBox *PlatformBase `json:"virtualbox"`
Vmware *PlatformBase `json:"vmware"`
Vultr *PlatformBase `json:"vultr"`
}
// PlatformBase with no cloud images
@ -71,6 +77,12 @@ type PlatformBase struct {
Artifacts map[string]ImageFormat `json:"artifacts"`
}
// PlatformAliyun contains Aliyun image information
type PlatformAliyun struct {
PlatformBase
Images map[string]CloudImage `json:"images"`
}
// PlatformAws contains AWS image information
type PlatformAws struct {
PlatformBase
@ -83,6 +95,18 @@ type PlatformGcp struct {
Image *GcpImage `json:"image"`
}
// PlatformIBMCloud IBMCloud/PowerVS image detail
type PlatformIBMCloud struct {
PlatformBase
Images map[string]IBMCloudImage `json:"images"`
}
// PlatformKubeVirt containerDisk metadata
type PlatformKubeVirt struct {
PlatformBase
Image *ContainerImage `json:"image"`
}
// ImageFormat contains all artifacts for a single OS image
type ImageFormat struct {
Disk *Artifact `json:"disk,omitempty"`
@ -104,9 +128,23 @@ type CloudImage struct {
Image string `json:"image"`
}
// ContainerImage represents a tagged container image
type ContainerImage struct {
// Preferred way to reference the image, which might be by tag or digest
Image string `json:"image"`
DigestRef string `json:"digest-ref"`
}
// GcpImage represents a GCP cloud image
type GcpImage struct {
Project string `json:"project,omitempty"`
Project string `json:"project"`
Family string `json:"family,omitempty"`
Name string `json:"name,omitempty"`
Name string `json:"name"`
}
// IBMCloudImage represents an IBMCloud/PowerVS cloud object - which is an ova image for PowerVS and a qcow for IBMCloud in the cloud object storage bucket
type IBMCloudImage struct {
Object string `json:"object"`
Bucket string `json:"bucket"`
Url string `json:"url"`
}

View file

@ -39,18 +39,37 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
if relRHCOSExt != nil {
rhcosExt = &rhcos.Extensions{}
}
if releaseArch.Media.Aliyun != nil {
artifacts["aliyun"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.Aliyun.Artifacts),
}
aliyunImages := stream.ReplicatedImage{
Regions: make(map[string]stream.SingleImage),
}
if releaseArch.Media.Aliyun.Images != nil {
for region, image := range releaseArch.Media.Aliyun.Images {
si := stream.SingleImage{Release: rel.Release, Image: image.Image}
aliyunImages.Regions[region] = si
}
cloudImages.Aliyun = &aliyunImages
}
}
if releaseArch.Media.Aws != nil {
artifacts["aws"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.Aws.Artifacts),
}
awsAmis := stream.AwsImage{
Regions: make(map[string]stream.AwsRegionImage),
awsAmis := stream.ReplicatedImage{
Regions: make(map[string]stream.SingleImage),
}
if releaseArch.Media.Aws.Images != nil {
for region, ami := range releaseArch.Media.Aws.Images {
ri := stream.AwsRegionImage{Release: rel.Release, Image: ami.Image}
awsAmis.Regions[region] = ri
si := stream.SingleImage{Release: rel.Release, Image: ami.Image}
awsAmis.Regions[region] = si
}
cloudImages.Aws = &awsAmis
@ -76,10 +95,10 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
// See https://github.com/coreos/stream-metadata-go/issues/13
}
if releaseArch.Media.Aliyun != nil {
artifacts["aliyun"] = stream.PlatformArtifacts{
if releaseArch.Media.AzureStack != nil {
artifacts["azurestack"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.Aliyun.Artifacts),
Formats: mapFormats(releaseArch.Media.AzureStack.Artifacts),
}
}
@ -105,6 +124,7 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
if releaseArch.Media.Gcp.Image != nil {
cloudImages.Gcp = &stream.GcpImage{
Release: rel.Release,
Name: releaseArch.Media.Gcp.Image.Name,
Family: releaseArch.Media.Gcp.Image.Family,
Project: releaseArch.Media.Gcp.Image.Project,
@ -112,6 +132,20 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
}
}
if releaseArch.Media.KubeVirt != nil {
artifacts["kubevirt"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.KubeVirt.Artifacts),
}
if releaseArch.Media.KubeVirt.Image != nil {
cloudImages.KubeVirt = &stream.ContainerImage{
Release: rel.Release,
Image: releaseArch.Media.KubeVirt.Image.Image,
DigestRef: releaseArch.Media.KubeVirt.Image.DigestRef,
}
}
}
if releaseArch.Media.Digitalocean != nil {
artifacts["digitalocean"] = stream.PlatformArtifacts{
Release: rel.Release,
@ -130,6 +164,22 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.Ibmcloud.Artifacts),
}
ibmcloudObjects := stream.ReplicatedObject{
Regions: make(map[string]stream.SingleObject),
}
if releaseArch.Media.Ibmcloud.Images != nil {
for region, object := range releaseArch.Media.Ibmcloud.Images {
so := stream.SingleObject{
Release: rel.Release,
Object: object.Object,
Bucket: object.Bucket,
Url: object.Url,
}
ibmcloudObjects.Regions[region] = so
}
cloudImages.Ibmcloud = &ibmcloudObjects
}
}
// if releaseArch.Media.Packet != nil {
@ -143,6 +193,13 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
// cloudImages.Packet = &packetImage
// }
if releaseArch.Media.Nutanix != nil {
artifacts["nutanix"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.Nutanix.Artifacts),
}
}
if releaseArch.Media.Openstack != nil {
artifacts["openstack"] = stream.PlatformArtifacts{
Release: rel.Release,
@ -150,6 +207,29 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
}
}
if releaseArch.Media.PowerVS != nil {
artifacts["powervs"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.PowerVS.Artifacts),
}
powervsObjects := stream.ReplicatedObject{
Regions: make(map[string]stream.SingleObject),
}
if releaseArch.Media.PowerVS.Images != nil {
for region, object := range releaseArch.Media.PowerVS.Images {
so := stream.SingleObject{
Release: rel.Release,
Object: object.Object,
Bucket: object.Bucket,
Url: object.Url,
}
powervsObjects.Regions[region] = so
}
cloudImages.PowerVS = &powervsObjects
}
}
if releaseArch.Media.Qemu != nil {
artifacts["qemu"] = stream.PlatformArtifacts{
Release: rel.Release,
@ -157,13 +237,19 @@ func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
}
}
// if releaseArch.Media.Virtualbox != nil {
// virtualbox := StreamMediaDetails{
// Release: rel.Release,
// Formats: releaseArch.Media.Virtualbox.Artifacts,
// }
// artifacts.Virtualbox = &virtualbox
// }
if releaseArch.Media.QemuSecex != nil {
artifacts["qemu-secex"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.QemuSecex.Artifacts),
}
}
if releaseArch.Media.VirtualBox != nil {
artifacts["virtualbox"] = stream.PlatformArtifacts{
Release: rel.Release,
Formats: mapFormats(releaseArch.Media.VirtualBox.Artifacts),
}
}
if releaseArch.Media.Vmware != nil {
artifacts["vmware"] = stream.PlatformArtifacts{

View file

@ -0,0 +1,95 @@
package stream
import (
"crypto/sha256"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
)
// Fetch an artifact, validating its checksum. If applicable,
// the artifact will not be decompressed. Does not
// validate GPG signature.
func (a *Artifact) Fetch(w io.Writer) error {
resp, err := http.Get(a.Location)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%s returned status: %s", a.Location, resp.Status)
}
hasher := sha256.New()
reader := io.TeeReader(resp.Body, hasher)
_, err = io.Copy(w, reader)
if err != nil {
return err
}
// Validate sha256 checksum
foundChecksum := fmt.Sprintf("%x", hasher.Sum(nil))
if a.Sha256 != foundChecksum {
return fmt.Errorf("checksum mismatch for %s; expected=%s found=%s", a.Location, a.Sha256, foundChecksum)
}
return nil
}
// Name returns the "basename" of the artifact, i.e. the contents
// after the last `/`. This can be useful when downloading to a file.
func (a *Artifact) Name() (string, error) {
loc, err := url.Parse(a.Location)
if err != nil {
return "", fmt.Errorf("failed to parse artifact url: %w", err)
}
// Note this one uses `path` since even on Windows URLs have forward slashes.
return path.Base(loc.Path), nil
}
// Download fetches the specified artifact and saves it to the target
// directory. The full file path will be returned as a string.
// If the target file path exists, it will be overwritten.
// If the download fails, the temporary file will be deleted.
func (a *Artifact) Download(destdir string) (string, error) {
name, err := a.Name()
if err != nil {
return "", err
}
destfile := filepath.Join(destdir, name)
w, err := os.CreateTemp(destdir, ".coreos-artifact-")
if err != nil {
return "", err
}
finalized := false
defer func() {
if !finalized {
// Ignore an error to unlink
_ = os.Remove(w.Name())
}
}()
if err := a.Fetch(w); err != nil {
return "", err
}
if err := w.Sync(); err != nil {
return "", err
}
if err := w.Chmod(0644); err != nil {
return "", err
}
if err := w.Close(); err != nil {
return "", err
}
if err := os.Rename(w.Name(), destfile); err != nil {
return "", err
}
finalized = true
return destfile, nil
}

View file

@ -17,6 +17,7 @@ type Stream struct {
// Metadata for a release or stream
type Metadata struct {
LastModified string `json:"last-modified"`
Generator string `json:"generator,omitempty"`
}
// Arch contains release details for a particular hardware architecture
@ -44,31 +45,72 @@ type ImageFormat struct {
// Artifact represents one image file, plus its metadata
type Artifact struct {
Location string `json:"location"`
Signature string `json:"signature"`
Signature string `json:"signature,omitempty"`
Sha256 string `json:"sha256"`
UncompressedSha256 string `json:"uncompressed-sha256,omitempty"`
}
// Images contains images available in cloud providers
type Images struct {
Aws *AwsImage `json:"aws,omitempty"`
Gcp *GcpImage `json:"gcp,omitempty"`
Aliyun *ReplicatedImage `json:"aliyun,omitempty"`
Aws *AwsImage `json:"aws,omitempty"`
Gcp *GcpImage `json:"gcp,omitempty"`
Ibmcloud *ReplicatedObject `json:"ibmcloud,omitempty"`
KubeVirt *ContainerImage `json:"kubevirt,omitempty"`
PowerVS *ReplicatedObject `json:"powervs,omitempty"`
}
// AwsImage represents an image across all AWS regions
type AwsImage struct {
Regions map[string]AwsRegionImage `json:"regions,omitempty"`
// ReplicatedImage represents an image in all regions of an AWS-like cloud
type ReplicatedImage struct {
Regions map[string]SingleImage `json:"regions,omitempty"`
}
// AwsRegionImage represents an image in one AWS region
type AwsRegionImage struct {
// SingleImage represents a globally-accessible image or an image in a
// single region of an AWS-like cloud
type SingleImage struct {
Release string `json:"release"`
Image string `json:"image"`
}
// ContainerImage represents a tagged container image
type ContainerImage struct {
Release string `json:"release"`
// Preferred way to reference the image, which might be by tag or digest
Image string `json:"image"`
DigestRef string `json:"digest-ref"`
}
// AwsImage is a typedef for backwards compatibility.
type AwsImage = ReplicatedImage
// AwsRegionImage is a typedef for backwards compatibility.
type AwsRegionImage = SingleImage
// RegionImage is a typedef for backwards compatibility.
type RegionImage = SingleImage
// GcpImage represents a GCP cloud image
type GcpImage struct {
Project string `json:"project,omitempty"`
Release string `json:"release"`
Project string `json:"project"`
Family string `json:"family,omitempty"`
Name string `json:"name,omitempty"`
Name string `json:"name"`
}
// ReplicatedObject represents an object in all regions of an IBMCloud-like
// cloud
type ReplicatedObject struct {
Regions map[string]SingleObject `json:"regions,omitempty"`
}
// SingleObject represents a globally-accessible cloud storage object, or
// an object in a single region of an IBMCloud-like cloud
type SingleObject struct {
Release string `json:"release"`
Object string `json:"object"`
Bucket string `json:"bucket"`
Url string `json:"url"`
}
// RegionObject is a typedef for backwards compatibility.
type RegionObject = SingleObject

View file

@ -17,9 +17,38 @@ func (st *Stream) GetArchitecture(archname string) (*Arch, error) {
return &archdata, nil
}
// GetAliyunRegionImage returns the release data (Image ID and release ID) for a particular
// architecture and region.
func (st *Stream) GetAliyunRegionImage(archname, region string) (*SingleImage, error) {
starch, err := st.GetArchitecture(archname)
if err != nil {
return nil, err
}
aliyunimages := starch.Images.Aliyun
if aliyunimages == nil {
return nil, fmt.Errorf("%s: No Aliyun images", st.FormatPrefix(archname))
}
var regionVal SingleImage
var ok bool
if regionVal, ok = aliyunimages.Regions[region]; !ok {
return nil, fmt.Errorf("%s: No Aliyun images in region %s", st.FormatPrefix(archname), region)
}
return &regionVal, nil
}
// GetAliyunImage returns the Aliyun image for a particular architecture and region.
func (st *Stream) GetAliyunImage(archname, region string) (string, error) {
regionVal, err := st.GetAliyunRegionImage(archname, region)
if err != nil {
return "", err
}
return regionVal.Image, nil
}
// GetAwsRegionImage returns the release data (AMI and release ID) for a particular
// architecture and region.
func (st *Stream) GetAwsRegionImage(archname, region string) (*AwsRegionImage, error) {
func (st *Stream) GetAwsRegionImage(archname, region string) (*SingleImage, error) {
starch, err := st.GetArchitecture(archname)
if err != nil {
return nil, err
@ -28,7 +57,7 @@ func (st *Stream) GetAwsRegionImage(archname, region string) (*AwsRegionImage, e
if awsimages == nil {
return nil, fmt.Errorf("%s: No AWS images", st.FormatPrefix(archname))
}
var regionVal AwsRegionImage
var regionVal SingleImage
var ok bool
if regionVal, ok = awsimages.Regions[region]; !ok {
return nil, fmt.Errorf("%s: No AWS images in region %s", st.FormatPrefix(archname), region)
@ -45,3 +74,21 @@ func (st *Stream) GetAMI(archname, region string) (string, error) {
}
return regionVal.Image, nil
}
// QueryDisk finds the singleton disk artifact for a given format and architecture.
func (st *Stream) QueryDisk(architectureName, artifactName, formatName string) (*Artifact, error) {
arch, err := st.GetArchitecture(architectureName)
if err != nil {
return nil, err
}
artifacts := arch.Artifacts[artifactName]
if artifacts.Release == "" {
return nil, fmt.Errorf("%s: artifact '%s' not found", st.FormatPrefix(architectureName), artifactName)
}
format := artifacts.Formats[formatName]
if format.Disk == nil {
return nil, fmt.Errorf("%s: artifact '%s' format '%s' disk not found", st.FormatPrefix(architectureName), artifactName, formatName)
}
return format.Disk, nil
}

4
vendor/modules.txt vendored
View file

@ -338,8 +338,8 @@ github.com/coreos/go-systemd/v22/dbus
github.com/coreos/go-systemd/v22/internal/dlopen
github.com/coreos/go-systemd/v22/journal
github.com/coreos/go-systemd/v22/sdjournal
# github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
## explicit; go 1.15
# github.com/coreos/stream-metadata-go v0.4.1
## explicit; go 1.17
github.com/coreos/stream-metadata-go/fedoracoreos
github.com/coreos/stream-metadata-go/fedoracoreos/internals
github.com/coreos/stream-metadata-go/release