1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/pkg/gcs/gcsClient.go
Siarhei Pazdniakou cd243ee542
feat(gcs): allow upload to gcs from steps (#3034)
* Upload reports to Google Cloud Storage bucket

* Added tests. Made fixes

* Update step generation. GCS client was moved to GeneralConfig

* Code was refactored

* Fixed issues

* Fixed issues

* Code correction due to PR comments

* Improved gcs client and integration tests

* Integrated gcp config. Updated step metadata

* Fixed issues. Added tests

* Added cpe, vault, aliases resolving for reporting parameters

* Added tests

* Uncommented DeferExitHandler. Removed useless comments

* fixed cloning of config

* Added comments for exported functions. Removed unused mock

* minor fix

* Implemented setting of report name via paramRef

* some refactoring. Writing tests

* Update pkg/config/reporting.go

* Update cmd/sonarExecuteScan_generated.go

* Apply suggestions from code review

* Update pkg/config/reporting.go

* Update pkg/config/reporting.go

* fixed removing valut secret files

* Update pkg/config/reporting.go

* restore order

* restore order

* Apply suggestions from code review

* go generate

* fixed tests

* Update resources/metadata/sonarExecuteScan.yaml

* Update resources.go

* Fixed tests. Code was regenerated

* changed somewhere gcp to gcs. Fixed one test

* move gcsSubFolder to input parameters

* fixed removing valut secret files

* minor fix in integration tests

* fix integration tests

Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com>
2021-12-15 15:07:47 +01:00

221 lines
5.7 KiB
Go

package gcs
import (
"context"
"io"
"os"
"path/filepath"
"cloud.google.com/go/storage"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
// Client is an interface to mock gcsClient
type Client interface {
UploadFile(bucketID string, sourcePath string, targetPath string) error
DownloadFile(bucketID string, sourcePath string, targetPath string) error
ListFiles(bucketID string) ([]string, error)
Close() error
}
// gcsClient provides functions to interact with google cloud storage API
type gcsClient struct {
context context.Context
envVars []EnvVar
client storage.Client
clientOptions []option.ClientOption
openFile func(name string) (io.ReadCloser, error)
createFile func(name string) (io.WriteCloser, error)
}
// EnvVar defines an environment variable incl. information about a potential modification to the variable
type EnvVar struct {
Name string
Value string
Modified bool
}
type gcsOption func(*gcsClient)
// WithEnvVars initializes env variables in gcsClient
func WithEnvVars(envVars []EnvVar) gcsOption {
return func(g *gcsClient) {
g.envVars = envVars
}
}
// WithEnvVars initializes the openFile function in gcsClient
func WithOpenFileFunction(openFile func(name string) (io.ReadCloser, error)) gcsOption {
return func(g *gcsClient) {
g.openFile = openFile
}
}
// WithEnvVars initializes the createFile function in gcsClient
func WithCreateFileFunction(createFile func(name string) (io.WriteCloser, error)) gcsOption {
return func(g *gcsClient) {
g.createFile = createFile
}
}
// WithEnvVars initializes the Google Cloud Storage client options
func WithClientOptions(opts ...option.ClientOption) gcsOption {
return func(g *gcsClient) {
g.clientOptions = append(g.clientOptions, opts...)
}
}
// Init intitializes the google cloud storage client
func NewClient(opts ...gcsOption) (*gcsClient, error) {
var (
defaultOpenFile = openFileFromFS
defaultCreateFile = createFileOnFS
)
ctx := context.Background()
gcsClient := &gcsClient{
context: ctx,
openFile: defaultOpenFile,
createFile: defaultCreateFile,
}
// options handling
for _, opt := range opts {
opt(gcsClient)
}
gcsClient.prepareEnv()
client, err := storage.NewClient(ctx, gcsClient.clientOptions...)
if err != nil {
return nil, errors.Wrapf(err, "bucket connection failed: %v", err)
}
gcsClient.client = *client
return gcsClient, nil
}
// UploadFile uploads a file into a google cloud storage bucket
func (g *gcsClient) UploadFile(bucketID string, sourcePath string, targetPath string) error {
target := g.client.Bucket(bucketID).Object(targetPath).NewWriter(g.context)
log.Entry().Debugf("uploading %v to %v\n", sourcePath, targetPath)
sourceFile, err := g.openFile(sourcePath)
if err != nil {
return errors.Wrapf(err, "could not open source file: %v", err)
}
defer sourceFile.Close()
if err := g.copy(sourceFile, target); err != nil {
return errors.Wrapf(err, "upload failed: %v", err)
}
if err := target.Close(); err != nil {
return errors.Wrapf(err, "closing bucket failed: %v", err)
}
return nil
}
// DownloadFile downloads a file from a google cloud storage bucket
func (g *gcsClient) DownloadFile(bucketID string, sourcePath string, targetPath string) error {
log.Entry().Debugf("downloading %v to %v\n", sourcePath, targetPath)
gcsReader, err := g.client.Bucket(bucketID).Object(sourcePath).NewReader(g.context)
if err != nil {
return errors.Wrapf(err, "could not open source file from a google cloud storage bucket: %v", err)
}
targetWriter, err := g.createFile(targetPath)
if err != nil {
return errors.Wrapf(err, "could not create target file: %v", err)
}
defer targetWriter.Close()
if err := g.copy(gcsReader, targetWriter); err != nil {
return errors.Wrapf(err, "download failed: %v", err)
}
if err := gcsReader.Close(); err != nil {
return errors.Wrapf(err, "closing bucket failed: %v", err)
}
return nil
}
// ListFiles lists all files in certain GCS bucket
func (g *gcsClient) ListFiles(bucketID string) ([]string, error) {
fileNames := []string{}
it := g.client.Bucket(bucketID).Objects(g.context, nil)
for {
attrs, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
fileNames = append(fileNames, attrs.Name)
}
return fileNames, nil
}
// Close closes the client and removes previously set environment variables
func (g *gcsClient) Close() error {
if err := g.client.Close(); err != nil {
return err
}
if err := g.cleanupEnv(); err != nil {
return err
}
return nil
}
func (g *gcsClient) copy(source io.Reader, target io.Writer) error {
if _, err := io.Copy(target, source); err != nil {
return err
}
return nil
}
// prepareEnv sets required environment variables in case they are not set yet
func (g *gcsClient) prepareEnv() {
for key, env := range g.envVars {
g.envVars[key].Modified = setenvIfEmpty(env.Name, env.Value)
}
}
// cleanupEnv removes environment variables set by prepareEnv
func (g *gcsClient) cleanupEnv() error {
for _, env := range g.envVars {
if err := removeEnvIfPreviouslySet(env.Name, env.Modified); err != nil {
return err
}
}
return nil
}
func setenvIfEmpty(env, val string) bool {
if len(os.Getenv(env)) == 0 {
os.Setenv(env, val)
return true
}
return false
}
func removeEnvIfPreviouslySet(env string, previouslySet bool) error {
if previouslySet {
if err := os.Setenv(env, ""); err != nil {
return err
}
}
return nil
}
func openFileFromFS(name string) (io.ReadCloser, error) {
return os.Open(name)
}
func createFileOnFS(name string) (io.WriteCloser, error) {
if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil {
return nil, err
}
return os.Create(name)
}