mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-10 23:57:45 +02:00
completed autocert implimentation for web-api
This commit is contained in:
parent
c757463a17
commit
c5ea09d8e1
@ -36,8 +36,8 @@
|
|||||||
{"name": "WEB_API_HTTPS_HOST", "value": "{HTTPS_HOST}"},
|
{"name": "WEB_API_HTTPS_HOST", "value": "{HTTPS_HOST}"},
|
||||||
{"name": "WEB_API_APP_PROJECT", "value": "{APP_PROJECT}"},
|
{"name": "WEB_API_APP_PROJECT", "value": "{APP_PROJECT}"},
|
||||||
{"name": "WEB_API_APP_BASE_URL", "value": "{APP_BASE_URL}"},
|
{"name": "WEB_API_APP_BASE_URL", "value": "{APP_BASE_URL}"},
|
||||||
{"name": "WEB_API_HOST_PRIMARY", "value": "{HOST_PRIMARY}"},
|
{"name": "WEB_API_APP_HOST_PRIMARY", "value": "{HOST_PRIMARY}"},
|
||||||
{"name": "WEB_API_HOST_NAMES", "value": "{HOST_NAMES}"},
|
{"name": "WEB_API_APP_HOST_NAMES", "value": "{HOST_NAMES}"},
|
||||||
{"name": "WEB_API_REDIS_HOST", "value": "{CACHE_HOST}"},
|
{"name": "WEB_API_REDIS_HOST", "value": "{CACHE_HOST}"},
|
||||||
{"name": "WEB_API_DB_HOST", "value": "{DB_HOST}"},
|
{"name": "WEB_API_DB_HOST", "value": "{DB_HOST}"},
|
||||||
{"name": "WEB_API_DB_USER", "value": "{DB_USER}"},
|
{"name": "WEB_API_DB_USER", "value": "{DB_USER}"},
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws"
|
awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws"
|
||||||
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
|
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
|
||||||
@ -84,13 +85,14 @@ func main() {
|
|||||||
Host string `default:"" envconfig:"HOST"`
|
Host string `default:"" envconfig:"HOST"`
|
||||||
ReadTimeout time.Duration `default:"5s" envconfig:"READ_TIMEOUT"`
|
ReadTimeout time.Duration `default:"5s" envconfig:"READ_TIMEOUT"`
|
||||||
WriteTimeout time.Duration `default:"5s" envconfig:"WRITE_TIMEOUT"`
|
WriteTimeout time.Duration `default:"5s" envconfig:"WRITE_TIMEOUT"`
|
||||||
|
DisableHTTP2 bool `default:"false" envconfig:"DISABLE_HTTP2"`
|
||||||
}
|
}
|
||||||
App struct {
|
App struct {
|
||||||
Name string `default:"web-api" envconfig:"NAME"`
|
Name string `default:"web-api" envconfig:"NAME"`
|
||||||
Project string `default:"" envconfig:"PROJECT"`
|
Project string `default:"" envconfig:"PROJECT"`
|
||||||
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://example-project.com"`
|
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://example-project.com"`
|
||||||
HostPrimary string `envconfig:"HOST_PRIMARY" example:"example-project.com"`
|
HostPrimary string `envconfig:"HOST_PRIMARY" example:"example-project.com"`
|
||||||
HostNames []string `envconfig:"HOST_NAMES" example:"subdomain.example-project.com"`
|
HostNames []string `envconfig:"HOST_NAMES" example:"subdomain.example-project.com"`
|
||||||
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
||||||
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
||||||
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
||||||
@ -116,12 +118,12 @@ func main() {
|
|||||||
AnalyticsRate float64 `default:"0.10" envconfig:"ANALYTICS_RATE"`
|
AnalyticsRate float64 `default:"0.10" envconfig:"ANALYTICS_RATE"`
|
||||||
}
|
}
|
||||||
Aws struct {
|
Aws struct {
|
||||||
AccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID"` // WEB_API_AWS_AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY_ID
|
AccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID"` // WEB_API_AWS_AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY_ID
|
||||||
SecretAccessKey string `envconfig:"AWS_SECRET_ACCESS_KEY" json:"-"` // don't print
|
SecretAccessKey string `envconfig:"AWS_SECRET_ACCESS_KEY" json:"-"` // don't print
|
||||||
Region string `default:"us-east-1" envconfig:"AWS_REGION"`
|
Region string `default:"us-east-1" envconfig:"AWS_REGION"`
|
||||||
S3BucketPrivate string `envconfig:"S3_BUCKET_PRIVATE"`
|
S3BucketPrivate string `envconfig:"S3_BUCKET_PRIVATE"`
|
||||||
S3BucketPublic string `envconfig:"S3_BUCKET_PUBLIC"`
|
S3BucketPublic string `envconfig:"S3_BUCKET_PUBLIC"`
|
||||||
SecretsManagerConfigPrefix string `default:"" envconfig:"SECRETS_MANAGER_CONFIG_PREFIX"`
|
SecretsManagerConfigPrefix string `default:"" envconfig:"SECRETS_MANAGER_CONFIG_PREFIX"`
|
||||||
|
|
||||||
// Get an AWS session from an implicit source if no explicit
|
// Get an AWS session from an implicit source if no explicit
|
||||||
// configuration is provided. This is useful for taking advantage of
|
// configuration is provided. This is useful for taking advantage of
|
||||||
@ -179,7 +181,6 @@ func main() {
|
|||||||
cfg.Aws.SecretsManagerConfigPrefix = filepath.Join(pts...)
|
cfg.Aws.SecretsManagerConfigPrefix = filepath.Join(pts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If base URL is empty, set the default value from the HTTP Host
|
// If base URL is empty, set the default value from the HTTP Host
|
||||||
if cfg.App.BaseUrl == "" {
|
if cfg.App.BaseUrl == "" {
|
||||||
baseUrl := cfg.HTTP.Host
|
baseUrl := cfg.HTTP.Host
|
||||||
@ -196,7 +197,6 @@ func main() {
|
|||||||
cfg.App.BaseUrl = baseUrl
|
cfg.App.BaseUrl = baseUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Log App Info
|
// Log App Info
|
||||||
|
|
||||||
@ -239,7 +239,6 @@ func main() {
|
|||||||
awsSession = awstrace.WrapSession(awsSession)
|
awsSession = awstrace.WrapSession(awsSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start Redis
|
// Start Redis
|
||||||
// Ensure the eviction policy on the redis cluster is set correctly.
|
// Ensure the eviction policy on the redis cluster is set correctly.
|
||||||
@ -310,7 +309,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer masterDb.Close()
|
defer masterDb.Close()
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Init new Authenticator
|
// Init new Authenticator
|
||||||
var authenticator *auth.Authenticator
|
var authenticator *auth.Authenticator
|
||||||
@ -324,30 +322,41 @@ func main() {
|
|||||||
log.Fatalf("main : Constructing authenticator : %+v", err)
|
log.Fatalf("main : Constructing authenticator : %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Init redirect middleware to ensure all requests go to the primary domain.
|
// Init redirect middleware to ensure all requests go to the primary domain.
|
||||||
baseSiteUrl, err := url.Parse(cfg.App.BaseUrl)
|
primaryDomain := cfg.App.HostPrimary
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("main : Parse App Base URL : %s : %+v", cfg.App.BaseUrl, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var primaryDomain string
|
// When primary host is not set, we can parse host from the base app URL.
|
||||||
if strings.Contains(baseSiteUrl.Host, ":") {
|
if primaryDomain == "" {
|
||||||
primaryDomain, _, err = net.SplitHostPort(baseSiteUrl.Host)
|
baseSiteUrl, err := url.Parse(cfg.App.BaseUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("main : SplitHostPort : %s : %+v", baseSiteUrl.Host, err)
|
log.Fatalf("main : Parse App Base URL : %s : %+v", cfg.App.BaseUrl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(baseSiteUrl.Host, ":") {
|
||||||
|
primaryDomain, _, err = net.SplitHostPort(baseSiteUrl.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("main : SplitHostPort : %s : %+v", baseSiteUrl.Host, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
primaryDomain = baseSiteUrl.Host
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
primaryDomain = baseSiteUrl.Host
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
||||||
DomainName: primaryDomain,
|
RedirectConfig: mid.RedirectConfig{
|
||||||
|
Code: http.StatusMovedPermanently,
|
||||||
|
Skipper: func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) bool {
|
||||||
|
if r.URL.Path == "/ping" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DomainName: primaryDomain,
|
||||||
HTTPSEnabled: (cfg.HTTPS.Host != ""),
|
HTTPSEnabled: (cfg.HTTPS.Host != ""),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start Tracing Support
|
// Start Tracing Support
|
||||||
th := fmt.Sprintf("%s:%d", cfg.Trace.Host, cfg.Trace.Port)
|
th := fmt.Sprintf("%s:%d", cfg.Trace.Host, cfg.Trace.Port)
|
||||||
@ -369,7 +378,6 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// ECS Task registration for services that don't use an AWS Elastic Load Balancer.
|
// ECS Task registration for services that don't use an AWS Elastic Load Balancer.
|
||||||
err = devops.EcsServiceTaskInit(log, awsSession)
|
err = devops.EcsServiceTaskInit(log, awsSession)
|
||||||
@ -377,7 +385,6 @@ func main() {
|
|||||||
log.Fatalf("main : Ecs Service Task init : %+v", err)
|
log.Fatalf("main : Ecs Service Task init : %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start API Service
|
// Start API Service
|
||||||
|
|
||||||
@ -448,7 +455,10 @@ func main() {
|
|||||||
// Enable autocert to store certs via Secret Manager.
|
// Enable autocert to store certs via Secret Manager.
|
||||||
secretPrefix := filepath.Join(cfg.Aws.SecretsManagerConfigPrefix, "autocert")
|
secretPrefix := filepath.Join(cfg.Aws.SecretsManagerConfigPrefix, "autocert")
|
||||||
|
|
||||||
cache, err := devops.NewSecretManagerAutocertCache(log, awsSession, secretPrefix)
|
// Local file cache to reduce requests hitting Secret Manager.
|
||||||
|
localCache := autocert.DirCache(os.TempDir())
|
||||||
|
|
||||||
|
cache, err := devops.NewSecretManagerAutocertCache(log, awsSession, secretPrefix, localCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("main : HTTPS : %+v", err)
|
log.Fatalf("main : HTTPS : %+v", err)
|
||||||
}
|
}
|
||||||
@ -459,11 +469,15 @@ func main() {
|
|||||||
Cache: cache,
|
Cache: cache,
|
||||||
}
|
}
|
||||||
api.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
|
api.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
|
||||||
|
api.TLSConfig.NextProtos = append(api.TLSConfig.NextProtos, acme.ALPNProto)
|
||||||
|
if !cfg.HTTPS.DisableHTTP2 {
|
||||||
|
api.TLSConfig.NextProtos = append(api.TLSConfig.NextProtos, "h2")
|
||||||
|
}
|
||||||
|
|
||||||
httpServers = append(httpServers, api)
|
httpServers = append(httpServers, api)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Printf("main : API Listening %s", cfg.HTTPS.Host)
|
log.Printf("main : API Listening %s with SSL cert for hosts %s", cfg.HTTPS.Host, strings.Join(hosts, ", "))
|
||||||
serverErrors <- api.ListenAndServeTLS("", "")
|
serverErrors <- api.ListenAndServeTLS("", "")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
||||||
DomainName: primaryDomain,
|
DomainName: primaryDomain,
|
||||||
HTTPSEnabled: (cfg.HTTPS.Host != ""),
|
HTTPSEnabled: (cfg.HTTPS.Host != ""),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@ package mid
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -25,7 +26,7 @@ type (
|
|||||||
// DomainNameRedirectConfig defines the details needed to apply redirects based on domain names.
|
// DomainNameRedirectConfig defines the details needed to apply redirects based on domain names.
|
||||||
DomainNameRedirectConfig struct {
|
DomainNameRedirectConfig struct {
|
||||||
RedirectConfig
|
RedirectConfig
|
||||||
DomainName string
|
DomainName string
|
||||||
HTTPSEnabled bool
|
HTTPSEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +51,12 @@ func DefaultSkipper(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
|
|
||||||
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||||
// See `HTTPSRedirect()`.
|
// See `HTTPSRedirect()`.
|
||||||
func DomainNameRedirect(config DomainNameRedirectConfig) web.Middleware {
|
func DomainNameRedirect(config DomainNameRedirectConfig) web.Middleware {
|
||||||
return redirect(config.RedirectConfig, func(scheme, host, uri string) (ok bool, url string) {
|
return redirect(config.RedirectConfig, func(scheme, host, uri string) (ok bool, url string) {
|
||||||
|
|
||||||
// Redirects http requests to https.
|
// Redirects http requests to https.
|
||||||
if config.HTTPSEnabled {
|
if config.HTTPSEnabled {
|
||||||
if ok = scheme != "https"; ok {
|
if ok = scheme != "https"; ok {
|
||||||
url = "https://" + host + uri
|
|
||||||
|
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,16 +64,15 @@ func DomainNameRedirect(config DomainNameRedirectConfig) web.Middleware {
|
|||||||
// Redirects all domain name alternatives to the primary hostname.
|
// Redirects all domain name alternatives to the primary hostname.
|
||||||
if host != config.DomainName {
|
if host != config.DomainName {
|
||||||
host = config.DomainName
|
host = config.DomainName
|
||||||
|
ok = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
url = scheme + "://" + host + uri
|
url = scheme + "://" + host + uri
|
||||||
|
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// HTTPSRedirect redirects http requests to https.
|
// HTTPSRedirect redirects http requests to https.
|
||||||
// For example, http://geeksinthewoods.com will be redirect to https://geeksinthewoods.com.
|
// For example, http://geeksinthewoods.com will be redirect to https://geeksinthewoods.com.
|
||||||
func HTTPSRedirect() web.Middleware {
|
func HTTPSRedirect() web.Middleware {
|
||||||
@ -83,7 +81,7 @@ func HTTPSRedirect() web.Middleware {
|
|||||||
|
|
||||||
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||||
// See `HTTPSRedirect()`.
|
// See `HTTPSRedirect()`.
|
||||||
func HTTPSRedirectWithConfig(config RedirectConfig)web.Middleware {
|
func HTTPSRedirectWithConfig(config RedirectConfig) web.Middleware {
|
||||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||||
if ok = scheme != "https"; ok {
|
if ok = scheme != "https"; ok {
|
||||||
url = "https://" + host + uri
|
url = "https://" + host + uri
|
||||||
@ -100,7 +98,7 @@ func HTTPSWWWRedirect() web.Middleware {
|
|||||||
|
|
||||||
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||||
// See `HTTPSWWWRedirect()`.
|
// See `HTTPSWWWRedirect()`.
|
||||||
func HTTPSWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
func HTTPSWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
||||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||||
if ok = scheme != "https" && host[:3] != www; ok {
|
if ok = scheme != "https" && host[:3] != www; ok {
|
||||||
url = "https://www." + host + uri
|
url = "https://www." + host + uri
|
||||||
@ -117,7 +115,7 @@ func HTTPSNonWWWRedirect() web.Middleware {
|
|||||||
|
|
||||||
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||||
// See `HTTPSNonWWWRedirect()`.
|
// See `HTTPSNonWWWRedirect()`.
|
||||||
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
||||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||||
if ok = scheme != "https"; ok {
|
if ok = scheme != "https"; ok {
|
||||||
if host[:3] == www {
|
if host[:3] == www {
|
||||||
@ -154,7 +152,7 @@ func NonWWWRedirect() web.Middleware {
|
|||||||
|
|
||||||
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||||
// See `NonWWWRedirect()`.
|
// See `NonWWWRedirect()`.
|
||||||
func NonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
func NonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
|
||||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||||
if ok = host[:3] == www; ok {
|
if ok = host[:3] == www; ok {
|
||||||
url = scheme + "://" + host[4:] + uri
|
url = scheme + "://" + host[4:] + uri
|
||||||
|
@ -7,26 +7,28 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/secretsmanager"
|
"github.com/aws/aws-sdk-go/service/secretsmanager"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecretManagerAutocertCache implements the autocert.Cache interface for AWS Secrets Manager that is used by Manager
|
// SecretManagerAutocertCache implements the autocert.Cache interface for AWS Secrets Manager that is used by Manager
|
||||||
// to store and retrieve previously obtained certificates and other account data as opaque blobs.
|
// to store and retrieve previously obtained certificates and other account data as opaque blobs.
|
||||||
type SecretManagerAutocertCache struct {
|
type SecretManagerAutocertCache struct {
|
||||||
awsSession *session.Session
|
awsSession *session.Session
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
secretPrefix string
|
secretPrefix string
|
||||||
|
cache autocert.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecretManagerAutocertCache provides the functionality to keep config files sync'd between running tasks and across deployments.
|
// NewSecretManagerAutocertCache provides the functionality to keep config files sync'd between running tasks and across deployments.
|
||||||
func NewSecretManagerAutocertCache(log *log.Logger, awsSession *session.Session, secretPrefix string ) (*SecretManagerAutocertCache, error) {
|
func NewSecretManagerAutocertCache(log *log.Logger, awsSession *session.Session, secretPrefix string, cache autocert.Cache) (*SecretManagerAutocertCache, error) {
|
||||||
return &SecretManagerAutocertCache{
|
return &SecretManagerAutocertCache{
|
||||||
awsSession,
|
awsSession,
|
||||||
log,
|
log,
|
||||||
secretPrefix,
|
secretPrefix,
|
||||||
|
cache,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +36,16 @@ func NewSecretManagerAutocertCache(log *log.Logger, awsSession *session.Session,
|
|||||||
// If there's no such key, Get returns ErrCacheMiss.
|
// If there's no such key, Get returns ErrCacheMiss.
|
||||||
func (c *SecretManagerAutocertCache) Get(ctx context.Context, key string) ([]byte, error) {
|
func (c *SecretManagerAutocertCache) Get(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
|
||||||
|
// Check short term cache.
|
||||||
|
if c.cache != nil {
|
||||||
|
v, err := c.cache.Get(ctx, key)
|
||||||
|
if err != nil && err != autocert.ErrCacheMiss {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
} else if len(v) > 0 {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svc := secretsmanager.New(c.awsSession)
|
svc := secretsmanager.New(c.awsSession)
|
||||||
|
|
||||||
secretID := filepath.Join(c.secretPrefix, key)
|
secretID := filepath.Join(c.secretPrefix, key)
|
||||||
@ -43,7 +55,7 @@ func (c *SecretManagerAutocertCache) Get(ctx context.Context, key string) ([]byt
|
|||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == secretsmanager.ErrCodeResourceNotFoundException {
|
if aerr, ok := err.(awserr.Error); ok && (aerr.Code() == secretsmanager.ErrCodeResourceNotFoundException || aerr.Code() == secretsmanager.ErrCodeInvalidRequestException) {
|
||||||
return nil, autocert.ErrCacheMiss
|
return nil, autocert.ErrCacheMiss
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +64,7 @@ func (c *SecretManagerAutocertCache) Get(ctx context.Context, key string) ([]byt
|
|||||||
|
|
||||||
log.Printf("AWS Secrets Manager : Secret %s found", secretID)
|
log.Printf("AWS Secrets Manager : Secret %s found", secretID)
|
||||||
|
|
||||||
return res.SecretBinary, nil
|
return []byte(*res.SecretString), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores the data in the cache under the specified key.
|
// Put stores the data in the cache under the specified key.
|
||||||
@ -70,28 +82,27 @@ func (c *SecretManagerAutocertCache) Put(ctx context.Context, key string, data [
|
|||||||
SecretString: aws.String(string(data)),
|
SecretString: aws.String(string(data)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if aerr, ok := err.(awserr.Error); !ok {
|
aerr, ok := err.(awserr.Error)
|
||||||
|
|
||||||
if aerr.Code() == secretsmanager.ErrCodeInvalidRequestException {
|
if ok && aerr.Code() == secretsmanager.ErrCodeInvalidRequestException {
|
||||||
// InvalidRequestException: You can't create this secret because a secret with this
|
// InvalidRequestException: You can't create this secret because a secret with this
|
||||||
// name is already scheduled for deletion.
|
// name is already scheduled for deletion.
|
||||||
|
|
||||||
// Restore secret after it was already previously deleted.
|
// Restore secret after it was already previously deleted.
|
||||||
_, err = svc.RestoreSecret(&secretsmanager.RestoreSecretInput{
|
_, err = svc.RestoreSecret(&secretsmanager.RestoreSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "autocert failed to restore secret %s", secretID)
|
return errors.Wrapf(err, "autocert failed to restore secret %s", secretID)
|
||||||
}
|
|
||||||
|
|
||||||
} else if aerr.Code() != secretsmanager.ErrCodeResourceExistsException {
|
|
||||||
return errors.Wrapf(err, "autocert failed to create secret %s", secretID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if !ok || aerr.Code() != secretsmanager.ErrCodeResourceExistsException {
|
||||||
|
return errors.Wrapf(err, "autocert failed to create secret %s", secretID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If where was a resource exists error for create, then need to update the secret instead.
|
// If where was a resource exists error for create, then need to update the secret instead.
|
||||||
_, err = svc.UpdateSecret(&secretsmanager.UpdateSecretInput{
|
_, err = svc.UpdateSecret(&secretsmanager.UpdateSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
SecretString: aws.String(string(data)),
|
SecretString: aws.String(string(data)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -103,6 +114,13 @@ func (c *SecretManagerAutocertCache) Put(ctx context.Context, key string, data [
|
|||||||
log.Printf("AWS Secrets Manager : Secret %s created", secretID)
|
log.Printf("AWS Secrets Manager : Secret %s created", secretID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.cache != nil {
|
||||||
|
err = c.cache.Put(ctx, key, data)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +134,7 @@ func (c *SecretManagerAutocertCache) Delete(ctx context.Context, key string) err
|
|||||||
|
|
||||||
// Create the new entry in AWS Secret Manager for the file.
|
// Create the new entry in AWS Secret Manager for the file.
|
||||||
_, err := svc.DeleteSecret(&secretsmanager.DeleteSecretInput{
|
_, err := svc.DeleteSecret(&secretsmanager.DeleteSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
|
|
||||||
// (Optional) Specifies that the secret is to be deleted without any recovery
|
// (Optional) Specifies that the secret is to be deleted without any recovery
|
||||||
// window. You can't use both this parameter and the RecoveryWindowInDays parameter
|
// window. You can't use both this parameter and the RecoveryWindowInDays parameter
|
||||||
@ -145,7 +163,14 @@ func (c *SecretManagerAutocertCache) Delete(ctx context.Context, key string) err
|
|||||||
return errors.Wrapf(err, "autocert failed to delete secret %s", secretID)
|
return errors.Wrapf(err, "autocert failed to delete secret %s", secretID)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("AWS Secrets Manager : Secret %s deleted for %s", secretID)
|
log.Printf("AWS Secrets Manager : Secret %s deleted", secretID)
|
||||||
|
|
||||||
|
if c.cache != nil {
|
||||||
|
err = c.cache.Delete(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func SyncCfgInit(log *log.Logger, awsSession *session.Session, secretPrefix, wat
|
|||||||
localfiles := make(map[string]time.Time)
|
localfiles := make(map[string]time.Time)
|
||||||
|
|
||||||
// Do the initial sync before starting file watch to download any existing configs.
|
// Do the initial sync before starting file watch to download any existing configs.
|
||||||
err := SyncCfgDir(log, awsSession, secretPrefix, watchDir, localfiles)
|
err := SyncCfgDir(log, awsSession, secretPrefix, watchDir, localfiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -41,7 +41,6 @@ func SyncCfgInit(log *log.Logger, awsSession *session.Session, secretPrefix, wat
|
|||||||
// Init the watch to wait for sync local files to Secret Manager.
|
// Init the watch to wait for sync local files to Secret Manager.
|
||||||
WatchCfgDir(log, awsSession, secretPrefix, watchDir, watcher, localfiles)
|
WatchCfgDir(log, awsSession, secretPrefix, watchDir, watcher, localfiles)
|
||||||
|
|
||||||
|
|
||||||
// Init ticker to sync remote files from Secret Manager locally at the defined interval.
|
// Init ticker to sync remote files from Secret Manager locally at the defined interval.
|
||||||
if syncInterval.Seconds() > 0 {
|
if syncInterval.Seconds() > 0 {
|
||||||
ticker := time.NewTicker(syncInterval)
|
ticker := time.NewTicker(syncInterval)
|
||||||
@ -52,7 +51,7 @@ func SyncCfgInit(log *log.Logger, awsSession *session.Session, secretPrefix, wat
|
|||||||
log.Println("AWS Secrets Manager : Checking for remote updates")
|
log.Println("AWS Secrets Manager : Checking for remote updates")
|
||||||
|
|
||||||
// Do the initial sync before starting file watch to download any existing configs.
|
// Do the initial sync before starting file watch to download any existing configs.
|
||||||
err := SyncCfgDir(log, awsSession, secretPrefix, watchDir, localfiles)
|
err := SyncCfgDir(log, awsSession, secretPrefix, watchDir, localfiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("AWS Secrets Manager : Remote sync error - %+v", err)
|
log.Printf("AWS Secrets Manager : Remote sync error - %+v", err)
|
||||||
}
|
}
|
||||||
@ -82,7 +81,7 @@ func SyncCfgDir(log *log.Logger, awsSession *session.Session, secretPrefix, watc
|
|||||||
for _, s := range res.SecretList {
|
for _, s := range res.SecretList {
|
||||||
|
|
||||||
// Skip any secret that does not have a matching prefix.
|
// Skip any secret that does not have a matching prefix.
|
||||||
if !strings.HasPrefix(*s.Name, secretPrefix) {
|
if !strings.HasPrefix(*s.Name, secretPrefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +191,7 @@ func handleWatchCfgEvent(log *log.Logger, awsSession *session.Session, secretPre
|
|||||||
|
|
||||||
// Restore secret after it was already previously deleted.
|
// Restore secret after it was already previously deleted.
|
||||||
_, err = svc.RestoreSecret(&secretsmanager.RestoreSecretInput{
|
_, err = svc.RestoreSecret(&secretsmanager.RestoreSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "file watcher failed to restore secret %s for %s", secretID, event.Name)
|
return errors.Wrapf(err, "file watcher failed to restore secret %s for %s", secretID, event.Name)
|
||||||
@ -205,7 +204,7 @@ func handleWatchCfgEvent(log *log.Logger, awsSession *session.Session, secretPre
|
|||||||
|
|
||||||
// If where was a resource exists error for create, then need to update the secret instead.
|
// If where was a resource exists error for create, then need to update the secret instead.
|
||||||
_, err = svc.UpdateSecret(&secretsmanager.UpdateSecretInput{
|
_, err = svc.UpdateSecret(&secretsmanager.UpdateSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
SecretString: aws.String(string(dat)),
|
SecretString: aws.String(string(dat)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -225,7 +224,7 @@ func handleWatchCfgEvent(log *log.Logger, awsSession *session.Session, secretPre
|
|||||||
|
|
||||||
// Create the new entry in AWS Secret Manager for the file.
|
// Create the new entry in AWS Secret Manager for the file.
|
||||||
_, err := svc.DeleteSecret(&secretsmanager.DeleteSecretInput{
|
_, err := svc.DeleteSecret(&secretsmanager.DeleteSecretInput{
|
||||||
SecretId: aws.String(secretID),
|
SecretId: aws.String(secretID),
|
||||||
|
|
||||||
// (Optional) Specifies that the secret is to be deleted without any recovery
|
// (Optional) Specifies that the secret is to be deleted without any recovery
|
||||||
// window. You can't use both this parameter and the RecoveryWindowInDays parameter
|
// window. You can't use both this parameter and the RecoveryWindowInDays parameter
|
||||||
|
@ -3,9 +3,6 @@ package devops
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/sethgrid/pester"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -33,15 +30,6 @@ func EcsServiceTaskInit(log *log.Logger, awsSession *session.Session) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := pester.Get("http://169.254.170.2/v2/metadata")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("http://169.254.170.2/v2/metadata failed", err.Error())
|
|
||||||
} else {
|
|
||||||
dat, _ := ioutil.ReadAll(res.Body)
|
|
||||||
res.Body.Close()
|
|
||||||
fmt.Println("http://169.254.170.2/v2/metadata, OK", string(dat))
|
|
||||||
}
|
|
||||||
|
|
||||||
var zoneArecNames = map[string][]string{}
|
var zoneArecNames = map[string][]string{}
|
||||||
if v := os.Getenv("ROUTE53_ZONES"); v != "" {
|
if v := os.Getenv("ROUTE53_ZONES"); v != "" {
|
||||||
dat, err := base64.RawURLEncoding.DecodeString(v)
|
dat, err := base64.RawURLEncoding.DecodeString(v)
|
||||||
@ -241,6 +229,15 @@ func RegisterEcsServiceTasksRoute53(log *log.Logger, awsSession *session.Session
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
res, err := pester.Get("http://169.254.170.2/v2/metadata")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("http://169.254.170.2/v2/metadata failed", err.Error())
|
||||||
|
} else {
|
||||||
|
dat, _ := ioutil.ReadAll(res.Body)
|
||||||
|
res.Body.Close()
|
||||||
|
fmt.Println("http://169.254.170.2/v2/metadata, OK", string(dat))
|
||||||
|
}
|
||||||
|
|
||||||
http://169.254.170.2/v2/metadata,
|
http://169.254.170.2/v2/metadata,
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
const (
|
const (
|
||||||
HeaderUpgrade = "Upgrade"
|
HeaderUpgrade = "Upgrade"
|
||||||
HeaderXForwardedFor = "X-Forwarded-For"
|
HeaderXForwardedFor = "X-Forwarded-For"
|
||||||
HeaderXForwardedProto = "X-Forwarded-Proto"
|
HeaderXForwardedProto = "X-Forwarded-Proto"
|
||||||
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
|
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
|
||||||
|
@ -27,11 +27,11 @@ type ServiceDeployFlags struct {
|
|||||||
Env string `validate:"oneof=dev stage prod" example:"dev"`
|
Env string `validate:"oneof=dev stage prod" example:"dev"`
|
||||||
|
|
||||||
// Optional flags.
|
// Optional flags.
|
||||||
EnableHTTPS bool `validate:"omitempty" example:"false"`
|
EnableHTTPS bool `validate:"omitempty" example:"false"`
|
||||||
ServiceHostPrimary string `validate:"omitempty" example:"example-project.com"`
|
ServiceHostPrimary string `validate:"omitempty" example:"example-project.com"`
|
||||||
ServiceHostNames cli.StringSlice `validate:"omitempty" example:"subdomain.example-project.com"`
|
ServiceHostNames cli.StringSlice `validate:"omitempty" example:"subdomain.example-project.com"`
|
||||||
S3BucketPrivateName string `validate:"omitempty" example:"saas-example-project-private"`
|
S3BucketPrivateName string `validate:"omitempty" example:"saas-example-project-private"`
|
||||||
S3BucketPublicName string `validate:"omitempty" example:"saas-example-project-public"`
|
S3BucketPublicName string `validate:"omitempty" example:"saas-example-project-public"`
|
||||||
|
|
||||||
ProjectRoot string `validate:"omitempty" example:"."`
|
ProjectRoot string `validate:"omitempty" example:"."`
|
||||||
ProjectName string ` validate:"omitempty" example:"example-project"`
|
ProjectName string ` validate:"omitempty" example:"example-project"`
|
||||||
@ -56,9 +56,9 @@ type serviceDeployRequest struct {
|
|||||||
GoModFile string `validate:"required"`
|
GoModFile string `validate:"required"`
|
||||||
GoModName string `validate:"required"`
|
GoModName string `validate:"required"`
|
||||||
|
|
||||||
EnableHTTPS bool `validate:"omitempty"`
|
EnableHTTPS bool `validate:"omitempty"`
|
||||||
ServiceHostPrimary string `validate:"omitempty,required_with=EnableHTTPS,fqdn"`
|
ServiceHostPrimary string `validate:"omitempty,required_with=EnableHTTPS,fqdn"`
|
||||||
ServiceHostNames []string `validate:"omitempty,dive,fqdn"`
|
ServiceHostNames []string `validate:"omitempty,dive,fqdn"`
|
||||||
|
|
||||||
AwsCreds awsCredentials `validate:"required,dive,required"`
|
AwsCreds awsCredentials `validate:"required,dive,required"`
|
||||||
|
|
||||||
|
@ -80,21 +80,21 @@ func NewServiceDeployRequest(log *log.Logger, flags ServiceDeployFlags) (*servic
|
|||||||
AwsCreds: awsCreds,
|
AwsCreds: awsCreds,
|
||||||
|
|
||||||
// Optional flags.
|
// Optional flags.
|
||||||
ProjectRoot: flags.ProjectRoot,
|
ProjectRoot: flags.ProjectRoot,
|
||||||
ProjectName: flags.ProjectName,
|
ProjectName: flags.ProjectName,
|
||||||
DockerFile: flags.DockerFile,
|
DockerFile: flags.DockerFile,
|
||||||
EnableHTTPS: flags.EnableHTTPS,
|
EnableHTTPS: flags.EnableHTTPS,
|
||||||
ServiceHostPrimary: flags.ServiceHostPrimary,
|
ServiceHostPrimary: flags.ServiceHostPrimary,
|
||||||
ServiceHostNames: flags.ServiceHostNames,
|
ServiceHostNames: flags.ServiceHostNames,
|
||||||
S3BucketPrivateName: flags.S3BucketPrivateName,
|
S3BucketPrivateName: flags.S3BucketPrivateName,
|
||||||
S3BucketPublicName: flags.S3BucketPublicName,
|
S3BucketPublicName: flags.S3BucketPublicName,
|
||||||
EnableLambdaVPC: flags.EnableLambdaVPC,
|
EnableLambdaVPC: flags.EnableLambdaVPC,
|
||||||
EnableEcsElb: flags.EnableEcsElb,
|
EnableEcsElb: flags.EnableEcsElb,
|
||||||
NoBuild: flags.NoBuild,
|
NoBuild: flags.NoBuild,
|
||||||
NoDeploy: flags.NoDeploy,
|
NoDeploy: flags.NoDeploy,
|
||||||
NoCache: flags.NoCache,
|
NoCache: flags.NoCache,
|
||||||
NoPush: flags.NoPush,
|
NoPush: flags.NoPush,
|
||||||
RecreateService: flags.RecreateService,
|
RecreateService: flags.RecreateService,
|
||||||
|
|
||||||
flags: flags,
|
flags: flags,
|
||||||
}
|
}
|
||||||
@ -403,6 +403,8 @@ func NewServiceDeployRequest(log *log.Logger, flags ServiceDeployFlags) (*servic
|
|||||||
"secretsmanager:GetSecretValue",
|
"secretsmanager:GetSecretValue",
|
||||||
"secretsmanager:CreateSecret",
|
"secretsmanager:CreateSecret",
|
||||||
"secretsmanager:UpdateSecret",
|
"secretsmanager:UpdateSecret",
|
||||||
|
"secretsmanager:RestoreSecret",
|
||||||
|
"secretsmanager:DeleteSecret",
|
||||||
},
|
},
|
||||||
Resource: "*",
|
Resource: "*",
|
||||||
},
|
},
|
||||||
@ -1780,7 +1782,7 @@ func ServiceDeploy(log *log.Logger, req *serviceDeployRequest) error {
|
|||||||
|
|
||||||
// Route 53 zone lookup when hostname is set. Supports both top level domains or sub domains.
|
// Route 53 zone lookup when hostname is set. Supports both top level domains or sub domains.
|
||||||
var zoneArecNames = map[string][]string{}
|
var zoneArecNames = map[string][]string{}
|
||||||
if req.ServiceHostPrimary != "" {
|
if req.ServiceHostPrimary != "" {
|
||||||
log.Println("Route 53 - Get or create hosted zones.")
|
log.Println("Route 53 - Get or create hosted zones.")
|
||||||
|
|
||||||
svc := route53.New(req.awsSession())
|
svc := route53.New(req.awsSession())
|
||||||
@ -2554,24 +2556,24 @@ func ServiceDeploy(log *log.Logger, req *serviceDeployRequest) error {
|
|||||||
|
|
||||||
// List of placeholders that can be used in task definition and replaced on deployment.
|
// List of placeholders that can be used in task definition and replaced on deployment.
|
||||||
placeholders := map[string]string{
|
placeholders := map[string]string{
|
||||||
"{SERVICE}": req.ServiceName,
|
"{SERVICE}": req.ServiceName,
|
||||||
"{RELEASE_IMAGE}": req.ReleaseImage,
|
"{RELEASE_IMAGE}": req.ReleaseImage,
|
||||||
"{ECS_CLUSTER}": req.EcsClusterName,
|
"{ECS_CLUSTER}": req.EcsClusterName,
|
||||||
"{ECS_SERVICE}": req.EcsServiceName,
|
"{ECS_SERVICE}": req.EcsServiceName,
|
||||||
"{AWS_REGION}": req.AwsCreds.Region,
|
"{AWS_REGION}": req.AwsCreds.Region,
|
||||||
"{AWS_LOGS_GROUP}": req.CloudWatchLogGroupName,
|
"{AWS_LOGS_GROUP}": req.CloudWatchLogGroupName,
|
||||||
"{AWS_AWS_S3_BUCKET_PRIVATE}": req.S3BucketPrivateName,
|
"{AWS_S3_BUCKET_PRIVATE}": req.S3BucketPrivateName,
|
||||||
"{S3_BUCKET_PUBLIC}": req.S3BucketPublicName,
|
"{AWS_S3_BUCKET_PUBLIC}": req.S3BucketPublicName,
|
||||||
"{ENV}": req.Env,
|
"{ENV}": req.Env,
|
||||||
"{DATADOG_APIKEY}": datadogApiKey,
|
"{DATADOG_APIKEY}": datadogApiKey,
|
||||||
"{DATADOG_ESSENTIAL}": "true",
|
"{DATADOG_ESSENTIAL}": "true",
|
||||||
"{HTTP_HOST}": "0.0.0.0:80",
|
"{HTTP_HOST}": "0.0.0.0:80",
|
||||||
"{HTTPS_HOST}": "", // Not enabled by default
|
"{HTTPS_HOST}": "", // Not enabled by default
|
||||||
|
|
||||||
"{APP_PROJECT}": req.ProjectName,
|
"{APP_PROJECT}": req.ProjectName,
|
||||||
"{APP_BASE_URL}": "", // Not set by default, requires a hostname to be defined.
|
"{APP_BASE_URL}": "", // Not set by default, requires a hostname to be defined.
|
||||||
"{HOST_PRIMARY}": req.ServiceHostPrimary,
|
"{HOST_PRIMARY}": req.ServiceHostPrimary,
|
||||||
"{HOST_NAMES}": strings.Join(req.ServiceHostNames, ","),
|
"{HOST_NAMES}": strings.Join(req.ServiceHostNames, ","),
|
||||||
|
|
||||||
"{CACHE_HOST}": "", // Not enabled by default
|
"{CACHE_HOST}": "", // Not enabled by default
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user