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 }