github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/platform/lambda/deploys.go (about)

     1  package lambda
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  
     7  	"github.com/apex/log"
     8  	"github.com/apex/up"
     9  	"github.com/apex/up/internal/colors"
    10  	"github.com/apex/up/internal/table"
    11  	"github.com/apex/up/internal/util"
    12  	"github.com/araddon/dateparse"
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/session"
    15  	"github.com/aws/aws-sdk-go/service/lambda"
    16  	humanize "github.com/dustin/go-humanize"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // ShowDeploys implementation.
    21  func (p *Platform) ShowDeploys(region string) error {
    22  	s := session.New(aws.NewConfig().WithRegion(region))
    23  	c := lambda.New(s)
    24  
    25  	stages, err := getCurrentVersions(c, p.config)
    26  	if err != nil {
    27  		return errors.Wrap(err, "fetching current versions")
    28  	}
    29  
    30  	versions, err := getVersions(c, p.config.Name)
    31  	if err != nil {
    32  		return errors.Wrap(err, "fetching versions")
    33  	}
    34  
    35  	versions = filterLatest(versions)
    36  	sortVersionsDesc(versions)
    37  	versions = filterN(versions, 25)
    38  	t := table.New()
    39  
    40  	t.AddRow(table.Row{
    41  		{Text: colors.Bold("Stage")},
    42  		{Text: colors.Bold("Version")},
    43  		{Text: colors.Bold("Author")},
    44  		{Text: colors.Bold("Size")},
    45  		{Text: colors.Bold("Date")},
    46  	})
    47  
    48  	t.AddRow(table.Row{
    49  		{
    50  			Span: 5,
    51  		},
    52  	})
    53  
    54  	for _, f := range versions {
    55  		if *f.Version != "$LATEST" {
    56  			addDeployment(t, f, stages)
    57  		}
    58  	}
    59  
    60  	defer util.Pad()()
    61  	t.Println()
    62  
    63  	return nil
    64  }
    65  
    66  // addDeployment adds the release to table.
    67  func addDeployment(t *table.Table, f *lambda.FunctionConfiguration, stages map[string]string) {
    68  	commit := f.Environment.Variables["UP_COMMIT"]
    69  	author := f.Environment.Variables["UP_AUTHOR"]
    70  	stage := *f.Environment.Variables["UP_STAGE"]
    71  	created := dateparse.MustParse(*f.LastModified)
    72  	date := util.RelativeDate(created)
    73  	version := *f.Version
    74  	current := stages[stage] == version
    75  
    76  	t.AddRow(table.Row{
    77  		{Text: formatStage(stage, current)},
    78  		{Text: colors.Gray(util.DefaultString(commit, version))},
    79  		{Text: colors.Gray(util.DefaultString(author, "–"))},
    80  		{Text: humanize.Bytes(uint64(*f.CodeSize))},
    81  		{Text: date},
    82  	})
    83  }
    84  
    85  // formatStage returns the stage string format.
    86  func formatStage(s string, current bool) string {
    87  	var c colors.Func
    88  
    89  	switch s {
    90  	case "production":
    91  		c = colors.Purple
    92  	default:
    93  		c = colors.Gray
    94  	}
    95  
    96  	s = c(s)
    97  
    98  	if current {
    99  		s = c("*") + " " + s
   100  	} else {
   101  		s = colors.Gray("\u0020") + " " + s
   102  	}
   103  
   104  	return s
   105  }
   106  
   107  // getCurrentVersions returns the current stage versions.
   108  func getCurrentVersions(c *lambda.Lambda, config *up.Config) (map[string]string, error) {
   109  	m := make(map[string]string)
   110  
   111  	for _, s := range config.Stages.List() {
   112  		if s.IsLocal() {
   113  			continue
   114  		}
   115  
   116  		res, err := c.GetAlias(&lambda.GetAliasInput{
   117  			FunctionName: &config.Name,
   118  			Name:         aws.String(s.Name),
   119  		})
   120  
   121  		if err != nil {
   122  			return nil, errors.Wrapf(err, "fetching %s alias", s.Name)
   123  		}
   124  
   125  		m[s.Name] = *res.FunctionVersion
   126  	}
   127  
   128  	return m, nil
   129  }
   130  
   131  // getVersions returns all function versions.
   132  func getVersions(c *lambda.Lambda, name string) (versions []*lambda.FunctionConfiguration, err error) {
   133  	var marker *string
   134  
   135  	log.Debug("fetching versions")
   136  	for {
   137  		res, err := c.ListVersionsByFunction(&lambda.ListVersionsByFunctionInput{
   138  			FunctionName: &name,
   139  			MaxItems:     aws.Int64(10000),
   140  			Marker:       marker,
   141  		})
   142  
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  
   147  		log.Debugf("fetched %d versions", len(res.Versions))
   148  		versions = append(versions, res.Versions...)
   149  
   150  		marker = res.NextMarker
   151  		if marker == nil {
   152  			break
   153  		}
   154  	}
   155  	log.Debug("fetched versions")
   156  
   157  	return
   158  }
   159  
   160  // filterN returns a slice of the first n versions.
   161  func filterN(in []*lambda.FunctionConfiguration, n int) (out []*lambda.FunctionConfiguration) {
   162  	for i, v := range in {
   163  		if i-1 == n {
   164  			break
   165  		}
   166  		out = append(out, v)
   167  	}
   168  	return
   169  }
   170  
   171  // filterLatest returns a slice without $LATEST.
   172  func filterLatest(in []*lambda.FunctionConfiguration) (out []*lambda.FunctionConfiguration) {
   173  	for _, v := range in {
   174  		if *v.Version != "$LATEST" {
   175  			out = append(out, v)
   176  		}
   177  	}
   178  	return
   179  }
   180  
   181  // sortVersionsDesc sorts versions descending.
   182  func sortVersionsDesc(versions []*lambda.FunctionConfiguration) {
   183  	sort.Slice(versions, func(i int, j int) bool {
   184  		a := mustParseInt(*versions[i].Version)
   185  		b := mustParseInt(*versions[j].Version)
   186  
   187  		return a > b
   188  	})
   189  }
   190  
   191  // mustParseInt returns an int from string.
   192  func mustParseInt(s string) int64 {
   193  	n, err := strconv.ParseInt(s, 10, 64)
   194  	if err != nil {
   195  		panic(errors.Wrapf(err, "parsing integer string %v", s))
   196  	}
   197  	return n
   198  }