github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/io/config_store.go (about)

     1  package io
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	"github.com/ghodss/yaml"
     8  	"github.com/olli-ai/jx/v2/pkg/util"
     9  	"github.com/olli-ai/jx/v2/pkg/vault"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // ConfigStore provides an interface for storing configs
    14  type ConfigStore interface {
    15  	// Write saves some secret data to the store
    16  	Write(name string, bytes []byte) error
    17  
    18  	// Read reads some secret data from the store
    19  	Read(name string) ([]byte, error)
    20  
    21  	// WriteObject writes a named object to the store
    22  	WriteObject(name string, object interface{}) error
    23  
    24  	// ReadObject reads an object from the store
    25  	ReadObject(name string, object interface{}) error
    26  }
    27  
    28  type fileStore struct {
    29  }
    30  
    31  // NewFileStore creates a ConfigStore that stores its data to the filesystem
    32  func NewFileStore() ConfigStore {
    33  	return &fileStore{}
    34  }
    35  
    36  // Write writes a secret to the filesystem
    37  func (f *fileStore) Write(fileName string, bytes []byte) error {
    38  	return ioutil.WriteFile(fileName, bytes, util.DefaultWritePermissions)
    39  }
    40  
    41  // WriteObject writes a secret to the filesystem in YAML format
    42  func (f *fileStore) WriteObject(fileName string, object interface{}) error {
    43  	y, err := yaml.Marshal(object)
    44  	if err != nil {
    45  		return errors.Wrapf(err, "unable to marshal object to yaml: %v", object)
    46  	}
    47  	return f.Write(fileName, y)
    48  }
    49  
    50  // Read reads a secret form the filesystem
    51  func (f *fileStore) Read(fileName string) ([]byte, error) {
    52  	return ioutil.ReadFile(fileName)
    53  }
    54  
    55  // ReadObject reads an object from the filesystem as yaml
    56  func (f *fileStore) ReadObject(fileName string, object interface{}) error {
    57  	data, err := f.Read(fileName)
    58  	if err != nil {
    59  		return errors.Wrapf(err, "unable to read %s", fileName)
    60  	}
    61  	return yaml.Unmarshal(data, object)
    62  }
    63  
    64  type vaultStore struct {
    65  	client vault.Client
    66  	path   string
    67  }
    68  
    69  // NewVaultStore creates a new store which stores its data in Vault
    70  func NewVaultStore(client vault.Client, path string) ConfigStore {
    71  	return &vaultStore{
    72  		client: client,
    73  		path:   path,
    74  	}
    75  }
    76  
    77  // Write store a secret in vault as an array of bytes
    78  func (v *vaultStore) Write(name string, bytes []byte) error {
    79  	data := map[string]interface{}{
    80  		"data": bytes,
    81  	}
    82  	_, err := v.client.Write(v.secretPath(name), data)
    83  	if err != nil {
    84  		return errors.Wrapf(err, "unable to write data for secret '%s' to vault", name)
    85  	}
    86  	return nil
    87  }
    88  
    89  // Read reads a secret from vault which was stored as an array of bytes
    90  func (v *vaultStore) Read(name string) ([]byte, error) {
    91  	secret, err := v.client.Read(v.secretPath(name))
    92  	if err != nil {
    93  		return nil, errors.Wrapf(err, "unable to read '%s' secret from vault", name)
    94  	}
    95  	data, ok := secret["data"]
    96  	if !ok {
    97  		return nil, fmt.Errorf("data not found for secret '%s'", name)
    98  	}
    99  
   100  	bytes, ok := data.([]byte)
   101  	if !ok {
   102  		return nil, fmt.Errorf("unable to convert the secret content '%s' to bytes", name)
   103  	}
   104  
   105  	return bytes, nil
   106  }
   107  
   108  // WriteObject writes a generic named object to vault
   109  func (v *vaultStore) WriteObject(name string, object interface{}) error {
   110  	y, err := yaml.Marshal(object)
   111  	if err != nil {
   112  		return errors.Wrapf(err, "unable to marshal object to yaml: %v", object)
   113  	}
   114  	_, err = v.client.WriteYaml(v.secretPath(name), string(y))
   115  	if err != nil {
   116  		return errors.Wrapf(err, "writing the %q secret in YAMl format to vault", name)
   117  	}
   118  	return nil
   119  }
   120  
   121  // ReadObject reads a generic named object from vault
   122  func (v *vaultStore) ReadObject(name string, object interface{}) error {
   123  	data, err := v.client.ReadYaml(v.secretPath(name))
   124  	if err != nil {
   125  		return errors.Wrapf(err, "reading the %q secret in YAMl fromat from vault", name)
   126  	}
   127  	return yaml.Unmarshal([]byte(data), object)
   128  }
   129  
   130  func (v *vaultStore) secretPath(name string) string {
   131  	return v.path + name
   132  }