policies/recipes/artifacts.go (87 lines of code) (raw):

// Copyright 2019 Google Inc. 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 recipes import ( "context" "fmt" "io" "net/http" "net/url" "path" "path/filepath" "cloud.google.com/go/storage" "github.com/GoogleCloudPlatform/osconfig/clog" "github.com/GoogleCloudPlatform/osconfig/external" "github.com/GoogleCloudPlatform/osconfig/util" "cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb" ) // fetchArtifacts takes in a slice of artifacts and downloads them into the specified directory, // Returns a map of artifact names to their new locations on the local disk. func fetchArtifacts(ctx context.Context, artifacts []*agentendpointpb.SoftwareRecipe_Artifact, directory string) (map[string]string, error) { localNames := make(map[string]string) for _, a := range artifacts { clog.Debugf(ctx, "Downloading artifact: %q", a) path, err := fetchArtifact(ctx, a, directory) if err != nil { return nil, err } localNames[a.Id] = path } return localNames, nil } func fetchArtifact(ctx context.Context, artifact *agentendpointpb.SoftwareRecipe_Artifact, directory string) (string, error) { var checksum, extension string var reader io.ReadCloser switch { case artifact.GetGcs() != nil: gcs := artifact.GetGcs() extension = path.Ext(gcs.Object) cl, err := storage.NewClient(ctx) if err != nil { return "", fmt.Errorf("error creating gcs client: %v", err) } defer cl.Close() reader, err = external.FetchGCSObject(ctx, cl, gcs.Bucket, gcs.Object, gcs.Generation) if err != nil { return "", fmt.Errorf("error fetching artifact %q from GCS: %v", artifact.Id, err) } defer reader.Close() case artifact.GetRemote() != nil: remote := artifact.GetRemote() uri, err := url.Parse(remote.Uri) if err != nil { return "", fmt.Errorf("Could not parse url %q for artifact %q", remote.Uri, artifact.Id) } extension = path.Ext(uri.Path) checksum = remote.Checksum cl := &http.Client{} reader, err = getHTTPArtifact(ctx, cl, *uri) if err != nil { return "", fmt.Errorf("error fetching artifact %q: %v", artifact.Id, err) } defer reader.Close() default: return "", fmt.Errorf("unknown artifact type for artifact %v", artifact.Id) } localPath := getStoragePath(directory, artifact.Id, extension) if _, err := util.AtomicWriteFileStream(reader, checksum, localPath, 0600); err != nil { return "", fmt.Errorf("Error downloading stream: %v", err) } return localPath, nil } func getHTTPArtifact(ctx context.Context, client *http.Client, uri url.URL) (io.ReadCloser, error) { if !isSupportedURL(uri) { return nil, fmt.Errorf("error, unsupported protocol scheme %s", uri.Scheme) } reader, err := external.FetchRemoteObjectHTTP(ctx, client, uri.String()) if err != nil { return nil, err } return reader, nil } func isSupportedURL(uri url.URL) bool { return (uri.Scheme == "http") || (uri.Scheme == "https") } func getStoragePath(directory, fname, extension string) string { localpath := filepath.Join(directory, fname) if extension != "" { localpath = localpath + extension } return localpath }