github.com/kubernetes-incubator/kube-aws@v0.16.4/cfnstack/assets.go (about)

     1  package cfnstack
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/kubernetes-incubator/kube-aws/fingerprint"
     6  	"github.com/kubernetes-incubator/kube-aws/logger"
     7  	"github.com/kubernetes-incubator/kube-aws/pkg/api"
     8  	"path/filepath"
     9  	"strings"
    10  )
    11  
    12  type Assets interface {
    13  	Merge(Assets) Assets
    14  	AsMap() map[api.AssetID]api.Asset
    15  	FindAssetByStackAndFileName(string, string) (api.Asset, error)
    16  	S3Prefix() string
    17  }
    18  
    19  func EmptyAssets() assetsImpl {
    20  	return assetsImpl{
    21  		underlying: map[api.AssetID]api.Asset{},
    22  	}
    23  }
    24  
    25  type assetsImpl struct {
    26  	s3Prefix   string
    27  	underlying map[api.AssetID]api.Asset
    28  }
    29  
    30  func (a assetsImpl) Merge(other Assets) Assets {
    31  	merged := map[api.AssetID]api.Asset{}
    32  
    33  	for k, v := range a.underlying {
    34  		merged[k] = v
    35  	}
    36  	for k, v := range other.AsMap() {
    37  		merged[k] = v
    38  	}
    39  
    40  	return assetsImpl{
    41  		s3Prefix:   other.S3Prefix(),
    42  		underlying: merged,
    43  	}
    44  }
    45  
    46  func (a assetsImpl) S3Prefix() string {
    47  	return a.s3Prefix
    48  }
    49  
    50  func (a assetsImpl) AsMap() map[api.AssetID]api.Asset {
    51  	return a.underlying
    52  }
    53  
    54  func (a assetsImpl) findAssetByID(id api.AssetID) (api.Asset, error) {
    55  	asset, ok := a.underlying[id]
    56  	if !ok {
    57  		ks := []string{}
    58  		for id, _ := range a.underlying {
    59  			k := fmt.Sprintf("%s/%s", id.StackName, id.Filename)
    60  			ks = append(ks, k)
    61  		}
    62  		logger.Debugf("dumping stored asset keys: %s", strings.Join(ks, ", "))
    63  		return asset, fmt.Errorf("[bug] failed to get the asset for the id \"%s\"", id)
    64  	}
    65  	return asset, nil
    66  }
    67  
    68  func (a assetsImpl) FindAssetByStackAndFileName(stack string, file string) (api.Asset, error) {
    69  	return a.findAssetByID(api.NewAssetID(stack, file))
    70  }
    71  
    72  type AssetsBuilder interface {
    73  	Add(filename string, content string) (api.Asset, error)
    74  	AddUserDataPart(userdata api.UserData, part string, assetName string) error
    75  	Build() Assets
    76  }
    77  
    78  type AssetsBuilderImpl struct {
    79  	AssetLocationProvider
    80  	assets map[api.AssetID]api.Asset
    81  }
    82  
    83  func (b *AssetsBuilderImpl) Add(filename string, content string) (api.Asset, error) {
    84  	loc, err := b.Locate(filename)
    85  	if err != nil {
    86  		return api.Asset{}, err
    87  	}
    88  
    89  	asset := api.Asset{
    90  		AssetLocation: *loc,
    91  		Content:       content,
    92  	}
    93  
    94  	b.assets[loc.ID] = asset
    95  	return asset, nil
    96  }
    97  
    98  func (b *AssetsBuilderImpl) AddUserDataPart(userdata api.UserData, part string, assetName string) error {
    99  	if p, ok := userdata.Parts[part]; ok {
   100  		content, err := p.Template()
   101  		if err != nil {
   102  			return err
   103  		}
   104  
   105  		filename := fmt.Sprintf("%s-%s", assetName, fingerprint.SHA256(content))
   106  		asset, err := b.Add(filename, content)
   107  		if err != nil {
   108  			return err
   109  		}
   110  		p.Asset = asset
   111  	}
   112  	return nil // it is not an error if part is not found
   113  }
   114  
   115  func (b *AssetsBuilderImpl) Build() Assets {
   116  	return assetsImpl{
   117  		s3Prefix:   b.S3Prefix(),
   118  		underlying: b.assets,
   119  	}
   120  }
   121  
   122  func NewAssetsBuilder(stackName string, s3URI string, region api.Region) (*AssetsBuilderImpl, error) {
   123  	uri, err := S3URIFromString(s3URI)
   124  
   125  	if err != nil {
   126  		return nil, fmt.Errorf("failed creating s3 assets locator for stack %s: %v", stackName, err)
   127  	}
   128  
   129  	return &AssetsBuilderImpl{
   130  		AssetLocationProvider: AssetLocationProvider{
   131  			s3URI:     uri,
   132  			region:    region,
   133  			stackName: stackName,
   134  		},
   135  		assets: map[api.AssetID]api.Asset{},
   136  	}, nil
   137  }
   138  
   139  type AssetLocationProvider struct {
   140  	s3URI     S3URI
   141  	region    api.Region
   142  	stackName string
   143  }
   144  
   145  func (p AssetLocationProvider) S3DirURI() string {
   146  	return fmt.Sprintf("%s/%s", p.s3URI.String(), p.stackName)
   147  }
   148  
   149  func (p AssetLocationProvider) Locate(filename string) (*api.AssetLocation, error) {
   150  	if filename == "" {
   151  		return nil, fmt.Errorf("Can't produce S3 location for empty filename")
   152  	}
   153  
   154  	relativePathComponents := []string{
   155  		p.stackName,
   156  		filename,
   157  	}
   158  
   159  	// key = s3uri's path component + stack name + filename
   160  	key := strings.Join(
   161  		append(p.s3URI.KeyComponents(), relativePathComponents...),
   162  		"/",
   163  	)
   164  
   165  	id := api.NewAssetID(p.stackName, filename)
   166  
   167  	return &api.AssetLocation{
   168  		ID:     id,
   169  		Key:    key,
   170  		Bucket: p.s3URI.Bucket(),
   171  		Path:   filepath.Join(relativePathComponents...),
   172  		Region: p.region,
   173  	}, nil
   174  }
   175  
   176  // S3Prefix returns BUCKET + / + S3 OBJECT KEY PREFIX whereas the prefix is that of all the assets locatable by this provider
   177  // For example, in case this provider is configured to locate assets for stack MYSTACK in S3 bucket MYBUCKET
   178  // due to that you've passed an S3 URI of `s3://MYBUCKET/MY/PREFIX` and the stack name of MYSTACK,
   179  // this func returns "MYBUCKET/MY/PREFIX/MYSTACK".
   180  func (p AssetLocationProvider) S3Prefix() string {
   181  	return fmt.Sprintf("%s/%s", p.s3URI.BucketAndKey(), p.stackName)
   182  }