release/cli/pkg/operations/upload.go (158 lines of code) (raw):
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// 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 operations
import (
"context"
"fmt"
"path/filepath"
"strings"
"github.com/aws/aws-sdk-go/aws"
docker "github.com/fsouza/go-dockerclient"
"golang.org/x/sync/errgroup"
"github.com/aws/eks-anywhere/release/cli/pkg/aws/s3"
"github.com/aws/eks-anywhere/release/cli/pkg/constants"
"github.com/aws/eks-anywhere/release/cli/pkg/helm"
"github.com/aws/eks-anywhere/release/cli/pkg/images"
releasetypes "github.com/aws/eks-anywhere/release/cli/pkg/types"
packagesutils "github.com/aws/eks-anywhere/release/cli/pkg/util/packages"
)
func UploadArtifacts(ctx context.Context, r *releasetypes.ReleaseConfig, eksaArtifacts releasetypes.ArtifactsTable, isBundleRelease bool) error {
fmt.Println("\n==========================================================")
fmt.Println(" Artifacts Upload")
fmt.Println("==========================================================")
if r.DryRun {
fmt.Println("Skipping artifacts upload in dry-run mode")
return nil
}
errGroup, ctx := errgroup.WithContext(ctx)
sourceEcrAuthConfig := r.SourceClients.ECR.AuthConfig
releaseEcrAuthConfig := r.ReleaseClients.ECRPublic.AuthConfig
var packagesSourceEcrAuthConfig *docker.AuthConfiguration
if packagesutils.NeedsPackagesAccountArtifacts(r) {
packagesSourceEcrAuthConfig = r.SourceClients.Packages.AuthConfig
}
var packagesReleaseEcrAuthConfig *docker.AuthConfiguration
if r.DevRelease && !r.DryRun {
packagesReleaseEcrAuthConfig = r.ReleaseClients.Packages.AuthConfig
}
packagesArtifacts := map[string][]releasetypes.Artifact{}
if isBundleRelease {
projectsInBundle := []string{"eks-anywhere-packages"}
for _, project := range projectsInBundle {
projectArtifacts, err := r.BundleArtifactsTable.Load(project)
if err != nil {
return fmt.Errorf("artifacts for project %s not found in bundle artifacts table", project)
}
packagesArtifacts[project] = projectArtifacts
}
}
eksaArtifacts.Range(func(k, v interface{}) bool {
artifacts := v.([]releasetypes.Artifact)
for _, artifact := range artifacts {
r, packagesArtifacts, artifact, sourceEcrAuthConfig, packagesSourceEcrAuthConfig, releaseEcrAuthConfig, packagesReleaseEcrAuthConfig := r, packagesArtifacts, artifact, sourceEcrAuthConfig, packagesSourceEcrAuthConfig, releaseEcrAuthConfig, packagesReleaseEcrAuthConfig
errGroup.Go(func() error {
if artifact.Archive != nil {
return handleArchiveUpload(ctx, r, artifact)
}
if artifact.Manifest != nil {
return handleManifestUpload(ctx, r, artifact)
}
if artifact.Image != nil {
return handleImageUpload(ctx, r, packagesArtifacts, artifact, sourceEcrAuthConfig, packagesSourceEcrAuthConfig, releaseEcrAuthConfig, packagesReleaseEcrAuthConfig)
}
return nil
})
}
return true
})
if err := errGroup.Wait(); err != nil {
return fmt.Errorf("uploading artifacts: %v", err)
}
fmt.Printf("%s Successfully uploaded artifacts\n", constants.SuccessIcon)
return nil
}
func handleArchiveUpload(_ context.Context, r *releasetypes.ReleaseConfig, artifact releasetypes.Artifact) error {
archiveFile := filepath.Join(artifact.Archive.ArtifactPath, artifact.Archive.ReleaseName)
fmt.Printf("Archive - %s\n", archiveFile)
key := filepath.Join(artifact.Archive.ReleaseS3Path, artifact.Archive.ReleaseName)
err := s3.UploadFile(archiveFile, aws.String(r.ReleaseBucket), aws.String(key), r.ReleaseClients.S3.Uploader, artifact.Archive.Private)
if err != nil {
return fmt.Errorf("uploading archive file [%s] to S3: %v", key, err)
}
checksumExtensions := []string{".sha256", ".sha512"}
// Adding a special case for tinkerbell/hook project.
// The project builds linux kernel files that are not stored as tarballs and currently do not have SHA checksums.
// TODO(pokearu): Add logic to generate SHA for hook project
if artifact.Archive.ProjectPath == constants.HookProjectPath {
checksumExtensions = []string{}
}
for _, extension := range checksumExtensions {
checksumFile := filepath.Join(artifact.Archive.ArtifactPath, artifact.Archive.ReleaseName) + extension
fmt.Printf("Checksum - %s\n", checksumFile)
key := filepath.Join(artifact.Archive.ReleaseS3Path, artifact.Archive.ReleaseName) + extension
err := s3.UploadFile(checksumFile, aws.String(r.ReleaseBucket), aws.String(key), r.ReleaseClients.S3.Uploader, artifact.Archive.Private)
if err != nil {
return fmt.Errorf("uploading checksum file [%s] to S3: %v", key, err)
}
}
return nil
}
func handleManifestUpload(_ context.Context, r *releasetypes.ReleaseConfig, artifact releasetypes.Artifact) error {
manifestFile := filepath.Join(artifact.Manifest.ArtifactPath, artifact.Manifest.ReleaseName)
fmt.Printf("Manifest - %s\n", manifestFile)
key := filepath.Join(artifact.Manifest.ReleaseS3Path, artifact.Manifest.ReleaseName)
err := s3.UploadFile(manifestFile, aws.String(r.ReleaseBucket), aws.String(key), r.ReleaseClients.S3.Uploader, artifact.Manifest.Private)
if err != nil {
return fmt.Errorf("uploading manifest file [%s] to S3: %v", key, err)
}
return nil
}
func handleImageUpload(_ context.Context, r *releasetypes.ReleaseConfig, packagesArtifacts map[string][]releasetypes.Artifact, artifact releasetypes.Artifact, defaultSourceEcrAuthConfig, packagesSourceEcrAuthConfig, defaultReleaseEcrAuthConfig, packagesReleaseEcrAuthConfig *docker.AuthConfiguration) error {
// If the artifact is a helm chart, skip the skopeo copy. Instead, modify the Chart.yaml to match the release tag
// and then use Helm package and push commands to upload chart to ECR Public
// Packages Helm chart modification for dev-release is handled elsewhere, so we are checking for that case and skipping
if !r.DryRun && ((strings.HasSuffix(artifact.Image.AssetName, "helm") || strings.HasSuffix(artifact.Image.AssetName, "chart")) && !(artifact.Image.AssetName == "eks-anywhere-packages-helm" && r.DevRelease)) {
// Trim -helm on the packages helm chart, but don't need to trim tinkerbell chart since the AssetName is the same as the repoName
trimmedAsset := strings.TrimSuffix(artifact.Image.AssetName, "-helm")
helmDriver, err := helm.NewHelm()
if err != nil {
return fmt.Errorf("creating helm client: %v", err)
}
fmt.Printf("Modifying helm chart for %s\n", trimmedAsset)
helmDest, err := helm.GetHelmDest(helmDriver, r, artifact.Image.SourceImageURI, trimmedAsset)
if err != nil {
return fmt.Errorf("getting Helm destination: %v", err)
}
fmt.Printf("Pulled helm chart locally to %s\n", helmDest)
err = helm.ModifyAndPushChartYaml(*artifact.Image, r, helmDriver, helmDest, packagesArtifacts, nil)
if err != nil {
return fmt.Errorf("modifying Chart.yaml and pushing Helm chart to destination: %v", err)
}
} else {
sourceImageUri := artifact.Image.SourceImageURI
releaseImageUri := artifact.Image.ReleaseImageURI
sourceEcrAuthConfig := defaultSourceEcrAuthConfig
sourceContainerRegistry := r.SourceContainerRegistry
var sourceEcrClient interface{}
if r.ReleaseEnvironment == "production" && r.BundleRelease {
sourceEcrClient = r.SourceClients.ECR.EcrPublicClient
} else {
sourceEcrClient = r.SourceClients.ECR.EcrClient
}
if packagesutils.NeedsPackagesAccountArtifacts(r) && (strings.Contains(sourceImageUri, "eks-anywhere-packages") || strings.Contains(sourceImageUri, "ecr-token-refresher") || strings.Contains(sourceImageUri, "credential-provider-package")) {
sourceEcrAuthConfig = packagesSourceEcrAuthConfig
sourceContainerRegistry = r.PackagesSourceContainerRegistry
sourceEcrClient = r.SourceClients.Packages.EcrClient
}
releaseEcrAuthConfig := defaultReleaseEcrAuthConfig
releaseContainerRegistry := r.ReleaseContainerRegistry
releaseEcrPublicClient := r.ReleaseClients.ECRPublic.Client
if r.DevRelease && !r.DryRun && (strings.Contains(releaseImageUri, "eks-anywhere-packages") || strings.Contains(releaseImageUri, "ecr-token-refresher") || strings.Contains(releaseImageUri, "credential-provider-package")) {
releaseEcrAuthConfig = packagesReleaseEcrAuthConfig
releaseContainerRegistry = r.PackagesReleaseContainerRegistry
releaseEcrPublicClient = r.ReleaseClients.Packages.Client
}
fmt.Printf("Source Image - %s\n", sourceImageUri)
fmt.Printf("Destination Image - %s\n", releaseImageUri)
err := images.CheckRepositoryImagesAndTagsCountLimit(sourceImageUri, releaseImageUri, sourceContainerRegistry, releaseContainerRegistry, sourceEcrClient, releaseEcrPublicClient)
if err != nil {
return fmt.Errorf("checking pushability of image [%s] based on destination repository images or tags limits: %v", releaseImageUri, err)
}
err = images.CopyToDestination(sourceEcrAuthConfig, releaseEcrAuthConfig, sourceImageUri, releaseImageUri)
if err != nil {
return fmt.Errorf("copying image from source [%s] to destination [%s]: %v", sourceImageUri, releaseImageUri, err)
}
}
return nil
}