github.com/ETCDEVTeam/janus@v0.2.4-0.20180611132348-f6c8fba730fa/gcp/gcp.go (about)

     1  package gcp
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"cloud.google.com/go/storage"
    17  	"google.golang.org/api/option"
    18  )
    19  
    20  var decryptedKeyFileName = "./" + strconv.Itoa(os.Getpid()) + "-gcloud.json"
    21  
    22  // Use: go run gcs-deploy.go -bucket builds.etcdevteam.com -object go-ethereum/$(cat version-base.txt)/geth-classic-$TRAVIS_OS_NAME-$(cat version-app.txt).zip -file geth-classic-linux-14.0.zip -key ./.gcloud.key
    23  
    24  // writeToGCP writes (uploads) a file or files to GCP Storage.
    25  // 'object' is the path at which 'file' will be written,
    26  // 'bucket' is the parent directory in which the object will be written.
    27  func writeToGCP(client *storage.Client, bucket, object, file string) error {
    28  
    29  	ctx := context.Background()
    30  	// [START upload_file]
    31  	f, err := os.Open(file)
    32  	if err != nil {
    33  		return err
    34  	}
    35  	defer f.Close()
    36  
    37  	// Write object to storage, ensuring basename for file/object if exists.
    38  	wc := client.Bucket(bucket).Object(object).NewWriter(ctx)
    39  	if _, err = io.Copy(wc, f); err != nil {
    40  		return err
    41  	}
    42  	if err := wc.Close(); err != nil {
    43  		return err
    44  	}
    45  	// [END upload_file]
    46  	fmt.Printf(`Successfully uploaded:
    47  	bucket: %v
    48  	object: %v
    49  	file: %v
    50  	`, bucket, object, file)
    51  	return nil
    52  }
    53  
    54  // SendToGCP sends a file or files to Google Cloud Provider storage
    55  // using a service account JSON key
    56  func SendToGCP(to, files, key string, gpg bool) error {
    57  
    58  	to = filepath.Clean(to)
    59  	files = filepath.Clean(files)
    60  	key = filepath.Clean(key)
    61  
    62  	// Ensure key file exists.
    63  	if _, e := os.Stat(key); e != nil {
    64  		return e
    65  	}
    66  
    67  	var arbitraryMap = make(map[string]interface{})
    68  
    69  	// Read key file
    70  	bRead, eRead := ioutil.ReadFile(key)
    71  	if eRead != nil {
    72  		return eRead
    73  	}
    74  
    75  	// Attempt to unmarshal key file, checks for encryption
    76  	e := json.Unmarshal(bRead, &arbitraryMap)
    77  	if e != nil {
    78  		fmt.Println("key is possibly encryped, attempting to decrypt with $GCP_PASSWD")
    79  
    80  		passwd := os.Getenv("GCP_PASSWD")
    81  		if passwd == "" {
    82  			return errors.New("env GCP_PASSWD not set, cannot decrypt")
    83  		}
    84  		// Assume reading for decoding error is it's encrypted... attempt to decrypt
    85  		if gpg {
    86  			// use pipe to securly provide password
    87  			cmd := exec.Command("gpg", "--batch", "--passphrase-fd", "0", "--decrypt", "--output", decryptedKeyFileName, key)
    88  			writer, err := cmd.StdinPipe()
    89  			if err != nil {
    90  				return err
    91  			}
    92  			go func() {
    93  				defer writer.Close()
    94  				io.WriteString(writer, passwd)
    95  			}()
    96  			err = cmd.Run()
    97  			if err != nil {
    98  				return err
    99  			}
   100  		} else {
   101  			if decryptError := exec.Command("openssl", "aes-256-cbc", "-pass", "env:GCP_PASSWD", "-in", key, "-out", decryptedKeyFileName, "-d").Run(); decryptError != nil {
   102  				return decryptError
   103  			}
   104  		}
   105  
   106  		fmt.Println("decrypted key file to: ", decryptedKeyFileName)
   107  		key = decryptedKeyFileName
   108  
   109  		// Only remove *unecrypted* key file
   110  		defer func() {
   111  			key = filepath.Clean(key)
   112  			p, pe := filepath.Abs(key)
   113  			if pe != nil {
   114  				fmt.Println(pe)
   115  			} else {
   116  				key = p
   117  			}
   118  
   119  			fmt.Printf(`
   120  				removing key: %v
   121  				`, key)
   122  			if errRm := os.Remove(key); errRm != nil {
   123  				fmt.Println(errRm)
   124  			}
   125  		}()
   126  	}
   127  
   128  	ctx := context.Background()
   129  	client, err := storage.NewClient(ctx, option.WithServiceAccountFile(key))
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// Use glob to get matching file paths.
   135  	globs, e := filepath.Glob(files)
   136  	if e != nil {
   137  		return e
   138  	}
   139  	// Ensure there is something to upload
   140  	if len(globs) == 0 {
   141  		return errors.New("no files matching '-to' pattern were found")
   142  	}
   143  	// Upload each file
   144  	for _, f := range globs {
   145  		fi, e := os.Stat(f)
   146  		if e != nil {
   147  			return e
   148  		}
   149  		if fi.IsDir() {
   150  			fmt.Printf("%s is a directory, continuing", fi.Name())
   151  			continue
   152  		}
   153  		// eg.
   154  		// to: builds.etcdevteam.com/go-ethereum/3.5.x
   155  		// file: ./dist/geth.zip
   156  		//
   157  		// Set bucket as first in separator-split path
   158  		// eg. builds.etcdevteam.com
   159  		bucket := strings.Split(filepath.ToSlash(to), "/")[0]
   160  
   161  		// Get relative path of 'to' based on 'bucket'
   162  		// eg. go-ethereum/3.5.x
   163  		object, relError := filepath.Rel(bucket, to)
   164  		if relError != nil {
   165  			return relError
   166  		}
   167  
   168  		// Append file to 'to' path.
   169  		// eg. go-ethereum/3.5.x/geth.zip
   170  		deployObject := filepath.Join(object, filepath.Base(f))
   171  		// Ensure actual '/' [slash]es are used, because Windows will make them '\' [backslashes]
   172  		// and google won't folder-ize the path
   173  		deployObject = filepath.ToSlash(deployObject)
   174  
   175  		// Send it.
   176  		if deployError := writeToGCP(client, bucket, deployObject, f); deployError != nil {
   177  			return deployError
   178  		}
   179  	}
   180  	return nil
   181  }