-
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
Support publishing CI artifacts to S3 #16659
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,47 @@ | ||
/* | ||
Copyright 2024 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 aws | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go-v2/config" | ||
"github.com/aws/aws-sdk-go-v2/service/s3" | ||
) | ||
|
||
func GetBucketRegion(ctx context.Context, bucket string) (string, error) { | ||
cfg, err := config.LoadDefaultConfig(ctx) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to load AWS configuration: %w", err) | ||
} | ||
client := s3.NewFromConfig(cfg) | ||
|
||
bucket = strings.TrimPrefix(bucket, "s3://") | ||
bucket = strings.Split(bucket, "/")[0] | ||
|
||
resp, err := client.HeadBucket(ctx, &s3.HeadBucketInput{ | ||
Bucket: aws.String(bucket), | ||
}) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get bucket region: %w", err) | ||
} | ||
|
||
return aws.ToString(resp.BucketRegion), nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ limitations under the License. | |
package builder | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"os" | ||
|
@@ -33,6 +34,7 @@ type BuildOptions struct { | |
KopsRoot string `flag:"-"` | ||
KubeRoot string `flag:"-"` | ||
StageLocation string `flag:"-"` | ||
S3BucketRegion string `flag:"-"` | ||
TargetBuildArch string `flag:"~target-build-arch" desc:"CPU architecture to test against"` | ||
BuildKubernetes bool `flag:"~build-kubernetes" desc:"Set this flag to true to build kubernetes"` | ||
} | ||
|
@@ -83,21 +85,37 @@ func (b *BuildOptions) Build() (*BuildResults, error) { | |
return results, nil | ||
} | ||
|
||
cmd := exec.Command("make", "gcs-publish-ci") | ||
var cmd exec.Cmd | ||
switch { | ||
case strings.HasPrefix(b.StageLocation, "gs://"): | ||
cmd = exec.Command("make", "gcs-publish-ci") | ||
cmd.SetEnv( | ||
fmt.Sprintf("GCS_LOCATION=%v", gcsLocation), | ||
) | ||
case strings.HasPrefix(b.StageLocation, "s3://"): | ||
if b.S3BucketRegion == "" { | ||
return nil, errors.New("missing required S3 bucket region") | ||
} | ||
cmd = exec.Command("make", "s3-publish-ci") | ||
cmd.SetEnv( | ||
fmt.Sprintf("S3_BUCKET=%v", gcsLocation), | ||
fmt.Sprintf("S3_REGION=%v", b.S3BucketRegion), | ||
) | ||
} | ||
cmd.SetEnv( | ||
fmt.Sprintf("HOME=%v", os.Getenv("HOME")), | ||
fmt.Sprintf("PATH=%v", os.Getenv("PATH")), | ||
fmt.Sprintf("GCS_LOCATION=%v", gcsLocation), | ||
fmt.Sprintf("GOPATH=%v", os.Getenv("GOPATH")), | ||
) | ||
|
||
cmd.SetDir(b.KopsRoot) | ||
exec.InheritOutput(cmd) | ||
if err := cmd.Run(); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Get the full path (including subdirectory) that we uploaded to | ||
// It is written by gcs-publish-ci to .build/upload/latest-ci.txt | ||
// It is written by the *-publish-ci make tasks to .build/upload/latest-ci.txt | ||
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. It might be nice to spell out the two tasks here (assuming we can't combine them), to make it more explicit and to help with searching... |
||
latestPath := filepath.Join(b.KopsRoot, ".build", "upload", "latest-ci.txt") | ||
kopsBaseURL, err := os.ReadFile(latestPath) | ||
if err != nil { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,13 +17,15 @@ limitations under the License. | |
package deployer | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path" | ||
"strings" | ||
|
||
"k8s.io/klog/v2" | ||
"k8s.io/kops/tests/e2e/kubetest2-kops/aws" | ||
"k8s.io/kops/tests/e2e/kubetest2-kops/gce" | ||
"k8s.io/kops/tests/e2e/pkg/util" | ||
"k8s.io/kops/tests/e2e/pkg/version" | ||
|
@@ -34,6 +36,7 @@ import ( | |
const ( | ||
defaultJobName = "pull-kops-e2e-kubernetes-aws" | ||
defaultGCSPath = "gs://k8s-staging-kops/pulls/%v/pull-%v" | ||
defaultS3Path = "s3://k8s-infra-kops-ci-results/pulls/%v/pull-%v" | ||
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. Created by kubernetes/k8s.io#6950 |
||
) | ||
|
||
func (d *deployer) Build() error { | ||
|
@@ -109,8 +112,8 @@ func (d *deployer) verifyBuildFlags() error { | |
d.BuildOptions.KubeRoot = KubeRoot | ||
} | ||
if d.StageLocation != "" { | ||
if !strings.HasPrefix(d.StageLocation, "gs://") { | ||
return errors.New("stage-location must be a gs:// path") | ||
if !strings.HasPrefix(d.StageLocation, "gs://") && !strings.HasPrefix(d.StageLocation, "s3://") { | ||
return errors.New("stage-location must be a gs:// or s3:// path") | ||
} | ||
} else if d.boskos != nil { | ||
d.StageLocation = d.stagingStore() | ||
|
@@ -119,14 +122,26 @@ func (d *deployer) verifyBuildFlags() error { | |
return err | ||
} | ||
} else { | ||
stageLocation, err := defaultStageLocation(d.KopsRoot) | ||
stageLocation, err := defaultStageLocation(d.CloudProvider, d.KopsRoot) | ||
if err != nil { | ||
return err | ||
} | ||
d.StageLocation = stageLocation | ||
} | ||
if strings.HasPrefix(d.StageLocation, "s3://") { | ||
region, err := aws.GetBucketRegion(context.TODO(), d.StageLocation) | ||
if err != nil { | ||
return fmt.Errorf("failed to get bucket region: %w", err) | ||
} | ||
d.BuildOptions.S3BucketRegion = region | ||
} | ||
if d.KopsBaseURL == "" && os.Getenv("KOPS_BASE_URL") == "" { | ||
d.KopsBaseURL = strings.Replace(d.StageLocation, "gs://", "https://storage.googleapis.com/", 1) | ||
switch { | ||
case strings.HasPrefix(d.StageLocation, "gs://"): | ||
d.KopsBaseURL = strings.Replace(d.StageLocation, "gs://", "https://storage.googleapis.com/", 1) | ||
case strings.HasPrefix(d.StageLocation, "s3://"): | ||
d.KopsBaseURL = fmt.Sprintf("https://s3.%s.amazonaws.com/%s", d.BuildOptions.S3BucketRegion, strings.TrimPrefix(d.StageLocation, "s3://")) | ||
} | ||
} | ||
|
||
if d.KopsVersionMarker != "" && !d.BuildOptions.BuildKubernetes { | ||
|
@@ -138,7 +153,7 @@ func (d *deployer) verifyBuildFlags() error { | |
return nil | ||
} | ||
|
||
func defaultStageLocation(kopsRoot string) (string, error) { | ||
func defaultStageLocation(cloudProvider, kopsRoot string) (string, error) { | ||
jobName := os.Getenv("JOB_NAME") | ||
if jobName == "" { | ||
jobName = defaultJobName | ||
|
@@ -156,5 +171,9 @@ func defaultStageLocation(kopsRoot string) (string, error) { | |
} | ||
sha = strings.TrimSpace(output[0]) | ||
} | ||
switch cloudProvider { | ||
case "aws": | ||
return fmt.Sprintf(defaultS3Path, jobName, sha), nil | ||
} | ||
return fmt.Sprintf(defaultGCSPath, jobName, sha), 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.
We do also have
hack/upload
, which might make this easier?