-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: add --coreos-url to oc adm release new
#21998
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package release | ||
|
||
// This package parses the HTTP API effectively | ||
// created by https://github.com/coreos/coreos-assembler | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// BuildMeta is a partial deserialization of the `meta.json` generated | ||
// by coreos-assembler for a build. | ||
type BuildMeta struct { | ||
AMIs []struct { | ||
HVM string `json:"hvm"` | ||
Name string `json:"name"` | ||
} `json:"amis"` | ||
BuildID string `json:"buildid"` | ||
Images struct { | ||
QEMU struct { | ||
Path string `json:"path"` | ||
SHA256 string `json:"sha256"` | ||
} `json:"qemu"` | ||
} `json:"images"` | ||
OSTreeVersion string `json:"ostree-version"` | ||
OSContainer struct { | ||
Digest string `json:"digest"` | ||
Image string `json:"image"` | ||
} `json:"oscontainer"` | ||
} | ||
|
||
// httpGetAll downloads a URL and gives you a byte array. | ||
func httpGetAll(ctx context.Context, url string) ([]byte, error) { | ||
var body []byte | ||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return body, errors.Wrap(err, "failed to build request") | ||
} | ||
|
||
client := &http.Client{} | ||
resp, err := client.Do(req.WithContext(ctx)) | ||
if err != nil { | ||
return body, errors.Wrapf(err, "failed to fetch %s", url) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return body, errors.Errorf("fetching %s status %s", url, resp.Status) | ||
} | ||
|
||
body, err = ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return body, errors.Wrap(err, "failed to read HTTP response") | ||
} | ||
|
||
return body, nil | ||
} | ||
|
||
// getLatestBuildVersion returns the latest CoreOS build version number | ||
func getLatestBuildVersion(ctx context.Context, baseURL string) (string, error) { | ||
var builds struct { | ||
Builds []string `json:"builds"` | ||
} | ||
buildsBuf, err := httpGetAll(ctx, baseURL + "/builds.json") | ||
if err != nil { | ||
return "", err | ||
} | ||
if err := json.Unmarshal(buildsBuf, &builds); err != nil { | ||
return "", errors.Wrap(err, "failed to parse HTTP response") | ||
} | ||
|
||
if len(builds.Builds) == 0 { | ||
return "", errors.Errorf("no builds found") | ||
} | ||
|
||
return builds.Builds[0], nil | ||
} | ||
|
||
// GetLatest returns the CoreOS build with target version. If version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// is the empty string, the latest will be used. | ||
func GetCoreOSBuild(ctx context.Context, baseURL string, version string) (*BuildMeta, error) { | ||
var err error | ||
if version == "" { | ||
version, err = getLatestBuildVersion(ctx, baseURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
buildUrl := fmt.Sprintf("%s/%s/meta.json", baseURL, version) | ||
buildStr, err := httpGetAll(ctx, buildUrl) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var build BuildMeta | ||
if err := json.Unmarshal(buildStr, &build); err != nil { | ||
return nil, errors.Wrap(err, "failed to parse HTTP response") | ||
} | ||
return &build, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"archive/tar" | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"compress/gzip" | ||
"encoding/json" | ||
"fmt" | ||
|
@@ -39,6 +40,12 @@ import ( | |
"github.com/openshift/origin/pkg/oc/cli/image/extract" | ||
) | ||
|
||
const ( | ||
// coreOSBootImageLabel contains JSON metadata from coreos-assembler. | ||
// In the future it may be used by the installer. | ||
coreOSBootImageLabel = "io.openshift.release.coreos-boot-image" | ||
) | ||
|
||
func NewNewOptions(streams genericclioptions.IOStreams) *NewOptions { | ||
return &NewOptions{ | ||
IOStreams: streams, | ||
|
@@ -102,6 +109,8 @@ func NewRelease(f kcmdutil.Factory, parentName string, streams genericclioptions | |
flags.StringVar(&o.FromDirectory, "from-dir", o.FromDirectory, "Use this directory as the source for the release payload.") | ||
flags.StringVar(&o.FromReleaseImage, "from-release", o.FromReleaseImage, "Use an existing release image as input.") | ||
flags.StringVar(&o.ReferenceMode, "reference-mode", o.ReferenceMode, "By default, the image reference from an image stream points to the public registry for the stream and the image digest. Pass 'source' to build references to the originating image.") | ||
flags.StringVar(&o.CoreOSURL, "coreos-url", o.CoreOSURL, "URL for CoreOS release server") | ||
flags.StringVar(&o.CoreOSVersion, "coreos-version", o.CoreOSVersion, "Choose this CoreOS version instead of picking latest in the stream") | ||
|
||
// properties of the release | ||
flags.StringVar(&o.Name, "name", o.Name, "The name of the release. Will default to the current time.") | ||
|
@@ -148,6 +157,8 @@ type NewOptions struct { | |
FromImageStream string | ||
Namespace string | ||
ReferenceMode string | ||
CoreOSURL string | ||
CoreOSVersion string | ||
|
||
Exclude []string | ||
AlwaysInclude []string | ||
|
@@ -538,6 +549,32 @@ func (o *NewOptions) Run() error { | |
is.Annotations = make(map[string]string) | ||
} | ||
|
||
if o.CoreOSURL != "" { | ||
coreosBuild, err := GetCoreOSBuild(context.TODO(), o.CoreOSURL, o.CoreOSVersion) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Fprintf(o.Out, "Using CoreOS build %s\n", coreosBuild.BuildID) | ||
digestedImage := fmt.Sprintf("%s@%s", coreosBuild.OSContainer.Image, coreosBuild.OSContainer.Digest) | ||
if digestedImage == "@" { | ||
return fmt.Errorf("No oscontainer in CoreOS build") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not do an explicit e.g. |
||
} | ||
|
||
// Hardcoded, this name was chosen in one of the machine-config-operator PRs | ||
// and added to the release payload by Clayton. | ||
o.Mappings = append(o.Mappings, Mapping{Source: "machine-os-content", | ||
Destination: digestedImage}) | ||
|
||
// And inject the full build metadata - primarily useful for | ||
// "bootimages" i.e. AMIs/qcow2/etc. | ||
serializedBuild, err := json.Marshal(coreosBuild) | ||
if err != nil { | ||
return err | ||
} | ||
// This is written as a label in the final image | ||
is.Annotations[coreOSBootImageLabel] = string(serializedBuild) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh OK, I see now why you wanted to also deserialize the qcow2s. Though... isn't there a way to have this data be a proper configmap instead of JSON-in-a-label? We were discussing this in openshift/machine-config-operator#183 right? (And including the pkglist, etc...). Anyway, totally fine doing it this way for now too! |
||
} | ||
|
||
// update any custom mappings and then sort the spec tags | ||
for _, m := range o.Mappings { | ||
if exclude.Has(m.Source) { | ||
|
@@ -930,6 +967,8 @@ func (o *NewOptions) write(r io.Reader, is *imageapi.ImageStream, now time.Time) | |
if len(dgst) > 0 { | ||
config.Config.Labels["io.openshift.release.base-image-digest"] = dgst.String() | ||
} | ||
config.Config.Labels[coreOSBootImageLabel] = is.Annotations[coreOSBootImageLabel] | ||
|
||
return nil | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just drop this and include only the keys we need for now. Not that we're planning to break the schema, though there's no point in deserializing it if we're not using it.
Speaking of, I think we discussed adding schema versioning to the JSON output. Probably would be good to do this before teaching the origin about it?