github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/config/config.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "os" 7 8 "github.com/apex/log" 9 "github.com/pkg/errors" 10 11 "github.com/apex/up/internal/header" 12 "github.com/apex/up/internal/inject" 13 "github.com/apex/up/internal/redirect" 14 "github.com/apex/up/internal/validate" 15 "github.com/apex/up/platform/aws/regions" 16 "github.com/aws/aws-sdk-go/aws/session" 17 ) 18 19 // defaulter is the interface that provides config defaulting. 20 type defaulter interface { 21 Default() error 22 } 23 24 // validator is the interface that provides config validation. 25 type validator interface { 26 Validate() error 27 } 28 29 // Config for the project. 30 type Config struct { 31 Name string `json:"name"` 32 Description string `json:"description"` 33 Type string `json:"type"` 34 Headers header.Rules `json:"headers"` 35 Redirects redirect.Rules `json:"redirects"` 36 Hooks Hooks `json:"hooks"` 37 Environment Environment `json:"environment"` 38 Regions []string `json:"regions"` 39 Profile string `json:"profile"` 40 Inject inject.Rules `json:"inject"` 41 Lambda Lambda `json:"lambda"` 42 CORS *CORS `json:"cors"` 43 ErrorPages ErrorPages `json:"error_pages"` 44 Proxy Relay `json:"proxy"` 45 Static Static `json:"static"` 46 Logs Logs `json:"logs"` 47 Stages Stages `json:"stages"` 48 DNS DNS `json:"dns"` 49 Alerting 50 } 51 52 // Validate implementation. 53 func (c *Config) Validate() error { 54 if err := validate.RequiredString(c.Name); err != nil { 55 return errors.Wrap(err, ".name") 56 } 57 58 if err := validate.Name(c.Name); err != nil { 59 return errors.Wrapf(err, ".name %q", c.Name) 60 } 61 62 if err := validate.List(c.Type, []string{"static", "server"}); err != nil { 63 return errors.Wrap(err, ".type") 64 } 65 66 if err := validate.Lists(c.Regions, regions.IDs); err != nil { 67 return errors.Wrap(err, ".regions") 68 } 69 70 if err := c.DNS.Validate(); err != nil { 71 return errors.Wrap(err, ".dns") 72 } 73 74 if err := c.Static.Validate(); err != nil { 75 return errors.Wrap(err, ".static") 76 } 77 78 if err := c.Inject.Validate(); err != nil { 79 return errors.Wrap(err, ".inject") 80 } 81 82 if err := c.Lambda.Validate(); err != nil { 83 return errors.Wrap(err, ".lambda") 84 } 85 86 if err := c.Proxy.Validate(); err != nil { 87 return errors.Wrap(err, ".proxy") 88 } 89 90 if err := c.Stages.Validate(); err != nil { 91 return errors.Wrap(err, ".stages") 92 } 93 94 if err := c.Alerting.Validate(); err != nil { 95 return err 96 } 97 98 if len(c.Regions) > 1 { 99 return errors.New("multiple regions is not yet supported, see https://github.com/apex/up/issues/134") 100 } 101 102 return nil 103 } 104 105 // Default implementation. 106 func (c *Config) Default() error { 107 if c.Stages == nil { 108 c.Stages = make(Stages) 109 } 110 111 // we default stages here before others simply to 112 // initialize the default stages such as "development" 113 // allowing runtime inference to default values. 114 if err := c.Stages.Default(); err != nil { 115 return errors.Wrap(err, ".stages") 116 } 117 118 // TODO: hack, move to the instantiation of aws clients 119 if c.Profile != "" { 120 setProfile(c.Profile) 121 } 122 123 // default type to server 124 if c.Type == "" { 125 c.Type = "server" 126 } 127 128 // runtime defaults 129 if c.Type != "static" { 130 runtime := inferRuntime() 131 log.WithField("type", runtime).Debug("inferred runtime") 132 133 if err := runtimeConfig(runtime, c); err != nil { 134 return errors.Wrap(err, "runtime") 135 } 136 } 137 138 // default .regions 139 if err := c.defaultRegions(); err != nil { 140 return errors.Wrap(err, ".region") 141 } 142 143 // region globbing 144 c.Regions = regions.Match(c.Regions) 145 146 // default .proxy 147 if err := c.Proxy.Default(); err != nil { 148 return errors.Wrap(err, ".proxy") 149 } 150 151 // default .lambda 152 if err := c.Lambda.Default(); err != nil { 153 return errors.Wrap(err, ".lambda") 154 } 155 156 // default .dns 157 if err := c.DNS.Default(); err != nil { 158 return errors.Wrap(err, ".dns") 159 } 160 161 // default .logs 162 if err := c.Logs.Default(); err != nil { 163 return errors.Wrap(err, ".logs") 164 } 165 166 // default .inject 167 if err := c.Inject.Default(); err != nil { 168 return errors.Wrap(err, ".inject") 169 } 170 171 // default .error_pages 172 if err := c.ErrorPages.Default(); err != nil { 173 return errors.Wrap(err, ".error_pages") 174 } 175 176 // default .stages 177 if err := c.Stages.Default(); err != nil { 178 return errors.Wrap(err, ".stages") 179 } 180 181 // default .alerting 182 if err := c.Alerting.Default(); err != nil { 183 return err 184 } 185 186 return nil 187 } 188 189 // Override with stage config if present, and re-validate. 190 func (c *Config) Override(stage string) error { 191 s := c.Stages.GetByName(stage) 192 if s == nil { 193 return nil 194 } 195 196 s.Override(c) 197 198 if err := c.Lambda.Validate(); err != nil { 199 return errors.Wrap(err, ".lambda") 200 } 201 202 return nil 203 } 204 205 // defaultRegions checks AWS_REGION and falls back on us-west-2. 206 func (c *Config) defaultRegions() error { 207 if len(c.Regions) != 0 { 208 log.Debugf("%d regions from config", len(c.Regions)) 209 return nil 210 } 211 212 s, err := session.NewSessionWithOptions(session.Options{ 213 SharedConfigState: session.SharedConfigEnable, 214 }) 215 216 if err != nil { 217 return errors.Wrap(err, "creating session") 218 } 219 220 if r := *s.Config.Region; r != "" { 221 log.Debugf("region from aws shared config %q", r) 222 c.Regions = append(c.Regions, r) 223 return nil 224 } 225 226 r := "us-west-2" 227 log.Debugf("region defaulted to %q", r) 228 c.Regions = append(c.Regions, r) 229 return nil 230 } 231 232 // ParseConfig returns config from JSON bytes. 233 func ParseConfig(b []byte) (*Config, error) { 234 c := &Config{} 235 236 if err := json.Unmarshal(b, c); err != nil { 237 return nil, errors.Wrap(err, "parsing json") 238 } 239 240 if err := c.Default(); err != nil { 241 return nil, errors.Wrap(err, "defaulting") 242 } 243 244 if err := c.Validate(); err != nil { 245 return nil, errors.Wrap(err, "validating") 246 } 247 248 return c, nil 249 } 250 251 // ParseConfigString returns config from JSON string. 252 func ParseConfigString(s string) (*Config, error) { 253 return ParseConfig([]byte(s)) 254 } 255 256 // MustParseConfigString returns config from JSON string. 257 func MustParseConfigString(s string) *Config { 258 c, err := ParseConfigString(s) 259 if err != nil { 260 panic(err) 261 } 262 263 return c 264 } 265 266 // ReadConfig reads the configuration from `path`. 267 func ReadConfig(path string) (*Config, error) { 268 b, err := ioutil.ReadFile(path) 269 if err != nil { 270 return nil, err 271 } 272 273 return ParseConfig(b) 274 } 275 276 // setProfile sets the AWS_PROFILE. 277 func setProfile(name string) { 278 os.Setenv("AWS_PROFILE", name) 279 }