-
Notifications
You must be signed in to change notification settings - Fork 502
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
push build: initial migration to krel #941
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 |
---|---|---|
|
@@ -17,9 +17,15 @@ limitations under the License. | |
package cmd | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"os" | ||
"os/user" | ||
|
||
"cloud.google.com/go/storage" | ||
"github.com/spf13/cobra" | ||
|
||
"k8s.io/release/pkg/release" | ||
) | ||
|
||
const description = ` | ||
|
@@ -125,16 +131,16 @@ func init() { | |
) | ||
pushBuildCmd.PersistentFlags().StringVar( | ||
&pushBuildOpts.releaseKind, | ||
"release-kind", | ||
"devel", | ||
"Specify an alternate bucket for pushes (normally 'devel' or 'ci')", | ||
) | ||
pushBuildCmd.PersistentFlags().StringVar( | ||
&pushBuildOpts.releaseType, | ||
"release-type", | ||
"kubernetes", | ||
"Kind of release to push to GCS. Supported values are kubernetes (default) or federation", | ||
) | ||
pushBuildCmd.PersistentFlags().StringVar( | ||
&pushBuildOpts.releaseType, | ||
"release-kind", | ||
"devel", | ||
"Specify an alternate bucket for pushes (normally 'devel' or 'ci')", | ||
) | ||
pushBuildCmd.PersistentFlags().StringVar( | ||
&pushBuildOpts.versionSuffix, | ||
"version-suffix", | ||
|
@@ -147,5 +153,89 @@ func init() { | |
|
||
func runPushBuild(opts *pushBuildOptions) error { | ||
log.Println("unimplemented") | ||
|
||
var latest string | ||
releaseKind := opts.releaseKind | ||
|
||
// Check if latest build uses bazel | ||
dir, err := os.Getwd() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
isBazel, err := release.BuiltWithBazel(dir, releaseKind) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if isBazel { | ||
log.Println("Using Bazel build 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. Its a nitty nit, but we could remove the dot at the end of a log to have a uniform logging layout. :) |
||
version, err := release.ReadBazelVersion(dir) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
latest = version | ||
} else { | ||
log.Println("Using Dockerized build version.") | ||
version, err := release.ReadDockerizedVersion(dir, releaseKind) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
latest = version | ||
} | ||
|
||
log.Println("Found build version: " + latest) | ||
|
||
valid, err := release.IsValidReleaseBuild(latest) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
if !valid { | ||
log.Fatal("build version is not valid for release") | ||
} | ||
|
||
if opts.ci && release.IsDirtyBuild(latest) { | ||
log.Fatal(`Refusing to push dirty build with --ci flag given.\n | ||
CI builds should always be performed from clean commits.`) | ||
} | ||
|
||
if opts.versionSuffix != "" { | ||
latest += "-" + opts.versionSuffix | ||
} | ||
|
||
gcsDest := opts.releaseType | ||
|
||
// TODO: is this how we want to handle gcs dest args? | ||
if opts.ci { | ||
gcsDest = "ci" | ||
} | ||
|
||
gcsDest += opts.gcsSuffix | ||
|
||
releaseBucket := opts.bucket | ||
if !rootOpts.nomock { | ||
u, err := user.Current() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
releaseBucket += "-" + u.Username | ||
} | ||
|
||
client, err := storage.NewClient(context.Background()) | ||
if err != nil { | ||
log.Fatalf("error fetching gcloud credentials %s... try running \"gcloud auth application-default login\"", err) | ||
} | ||
|
||
bucket := client.Bucket(releaseBucket) | ||
if bucket == nil { | ||
log.Fatalf("unable to identify specified bucket for artifacts: %s", releaseBucket) | ||
} | ||
|
||
// Check if bucket exists. | ||
if _, err = bucket.Attrs(context.Background()); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = ["release.go"], | ||
importpath = "k8s.io/release/pkg/release", | ||
visibility = ["//visibility:public"], | ||
deps = ["//pkg/util:go_default_library"], | ||
) | ||
|
||
filegroup( | ||
name = "package-srcs", | ||
srcs = glob(["**"]), | ||
tags = ["automanaged"], | ||
visibility = ["//visibility:private"], | ||
) | ||
|
||
filegroup( | ||
name = "all-srcs", | ||
srcs = [":package-srcs"], | ||
tags = ["automanaged"], | ||
visibility = ["//visibility:public"], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package release | ||
|
||
import ( | ||
"io/ioutil" | ||
"regexp" | ||
"strings" | ||
|
||
"k8s.io/release/pkg/util" | ||
) | ||
|
||
const ( | ||
versionReleaseRE = `v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[a-zA-Z0-9]+)*\.*(0|[1-9][0-9]*)?` | ||
versionDotZeroRE = `v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.0$` | ||
versionBuildRE = `([0-9]{1,})\+([0-9a-f]{5,40})` | ||
versionDirtyRE = `(-dirty)` | ||
dockerBuildPath = "/_output/release-tars/" | ||
bazelBuildPath = "/bazel-bin/build/release-tars/" | ||
bazelVersionPath = "/bazel-genfiles/version" | ||
dockerVersionPath = "/version" | ||
tarballExtension = ".tar.gz" | ||
) | ||
|
||
// BuiltWithBazel determines whether the most recent release was built with Bazel. | ||
func BuiltWithBazel(path string, releaseKind string) (bool, error) { | ||
bazelBuild := path + bazelBuildPath + releaseKind + tarballExtension | ||
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. I suggest using |
||
dockerBuild := path + dockerBuildPath + releaseKind + tarballExtension | ||
return util.MoreRecent(bazelBuild, dockerBuild) | ||
} | ||
|
||
// ReadBazelVersion reads the version from a Bazel build. | ||
func ReadBazelVersion(path string) (string, error) { | ||
version, err := ioutil.ReadFile(path + bazelVersionPath) | ||
return string(version), err | ||
} | ||
|
||
// ReadDockerizedVersion reads the version from a Dockerized | ||
func ReadDockerizedVersion(path, releaseKind string) (string, error) { | ||
dockerTarball := path + dockerBuildPath + releaseKind + tarballExtension | ||
return util.UntarAndRead(dockerTarball, releaseKind+dockerVersionPath) | ||
} | ||
|
||
// IsValidReleaseBuild checks if build version is valid for release. | ||
func IsValidReleaseBuild(build string) (bool, error) { | ||
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. I'd suggest in returning either a 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. I was initially thinking that as well, but I didn't know if there would be a use case for this function in the future where it might be valid to know if there was not a match or if the comparison failed altogether. If we don't think this is necessary, I think a good fix would be to use 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. Hm I see your point, so I guess we can also keep the current approach. :) |
||
return regexp.MatchString("("+versionReleaseRE+`(\.`+versionBuildRE+")?"+versionDirtyRE+"?)", build) | ||
} | ||
|
||
// IsDirtyBuild checks if build version is dirty. | ||
func IsDirtyBuild(build string) bool { | ||
return strings.Contains(build, "dirty") | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -17,13 +17,17 @@ limitations under the License. | |||
package util | ||||
|
||||
import ( | ||||
"archive/tar" | ||||
"bufio" | ||||
"compress/gzip" | ||||
"errors" | ||||
"fmt" | ||||
"io" | ||||
"io/ioutil" | ||||
"log" | ||||
"os" | ||||
"path/filepath" | ||||
"strings" | ||||
) | ||||
|
||||
/* | ||||
|
@@ -104,7 +108,7 @@ func FakeGOPATH(srcDir string) (string, error) { | |||
log.Printf("GOPATH: %s", os.Getenv("GOPATH")) | ||||
|
||||
gitRoot := fmt.Sprintf("%s/src/k8s.io", baseDir) | ||||
if err := os.MkdirAll(gitRoot, 0o755); err != nil { | ||||
if err := os.MkdirAll(gitRoot, os.FileMode(int(0755))); err != nil { | ||||
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. Hm, what is the benefit of doing it like this? 🙃 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. I was getting a compiler error on my local, but I think my config was just wrong. I'll change back :) 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. Okay, maybe you have no go 1.13 on your local machine? |
||||
return "", err | ||||
} | ||||
gitRoot = filepath.Join(gitRoot, "kubernetes") | ||||
|
@@ -122,3 +126,47 @@ func FakeGOPATH(srcDir string) (string, error) { | |||
|
||||
return gitRoot, nil | ||||
} | ||||
|
||||
// UntarAndRead opens a tarball and reads contents of a file inside. | ||||
func UntarAndRead(tarPath, filePath string) (string, error) { | ||||
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. Do we wanna write some tests for this function and 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. Definitely! In my check list above 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. nits:
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. Agree to both of these points :) This structure was mostly a result of trying to mimic the exact behavior of Line 131 in 471139d
|
||||
file, err := os.Open(tarPath) | ||||
if err != nil { | ||||
return "", err | ||||
} | ||||
|
||||
archive, err := gzip.NewReader(file) | ||||
if err != nil { | ||||
return "", err | ||||
} | ||||
tr := tar.NewReader(archive) | ||||
|
||||
for { | ||||
h, err := tr.Next() | ||||
if err == io.EOF { | ||||
break // End of archive | ||||
} | ||||
|
||||
if h.Name == filePath { | ||||
file, err := ioutil.ReadAll(tr) | ||||
return strings.TrimSuffix(string(file), "\n"), err | ||||
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 this (trimming of a newline) a general thing we need to do when extracting a file from a tarball, or is this specific to our case (reading that version file)? 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. This is more of a specific use-case thing, which, if this function is made more general as suggested above, should probably be deferred to the user of the function (i.e. the push build cmd). Thanks for pointing this out! |
||||
} | ||||
} | ||||
|
||||
return "", errors.New("unable to find file in tarball") | ||||
} | ||||
|
||||
// MoreRecent determines if file at path a was modified more recently than file at path b. | ||||
func MoreRecent(a, b string) (bool, error) { | ||||
// TODO: default to existing file if only one exists? | ||||
fileA, err := os.Stat(a) | ||||
if err != nil { | ||||
return false, err | ||||
} | ||||
|
||||
fileB, err := os.Stat(b) | ||||
if err != nil { | ||||
return false, err | ||||
} | ||||
|
||||
return (fileA.ModTime().Unix() > fileB.ModTime().Unix()), 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.
Should we let the errors bubble up instead of exiting via
log.Fatal
here (and at the other places)?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.
Do you mean that we should keep executing? I think it depends on the error. This one I think we should exit immediately because we will be unable to reliably push a build if we cannot get the working dir.
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.
What I meant is:
log.Fatal(...)
os.Exit(...)
or such somewhere centrally.I would not expect a function that returns an error also to shut down the whole process. If nothing else, that is IMHO not really intuitive and
hardstupid to test.