Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
var defaultFallbackDir string

const defaultFallbackFileMaxAge = 14 * 24 * time.Hour // 14 days
const defaultLivenessPingIntervalSeconds = 60 * 5 * time.Second

var secretsToInclude []string

Expand Down Expand Up @@ -80,6 +81,7 @@ doppler run --mount secrets.json -- cat secrets.json`,
exitOnWriteFailure := !utils.GetBoolFlag(cmd, "no-exit-on-write-failure")
preserveEnv := cmd.Flag("preserve-env").Value.String()
forwardSignals := utils.GetBoolFlag(cmd, "forward-signals")
enableLivenessPing := !fallbackOnly && !utils.GetBoolFlag(cmd, "no-liveness-ping")
localConfig := configuration.LocalConfig(cmd)
dynamicSecretsTTL := utils.GetDurationFlag(cmd, "dynamic-ttl")
exitOnMissingIncludedSecrets := !cmd.Flags().Changed("no-exit-on-missing-only-secrets")
Expand Down Expand Up @@ -223,6 +225,25 @@ doppler run --mount secrets.json -- cat secrets.json`,
// this variable has the potential to be racey, but is made safe by our use of the mutex
terminatedByWatch := false

startLivenessPing := func() {
ticker := time.NewTicker(defaultLivenessPingIntervalSeconds)

go func() {
for {
select {
case <-ticker.C:
_, err := controllers.LivenessPing(localConfig)
if !err.IsNil() {
// If we fail the liveness ping, we'll just log it for debugging, but it's likely an intermittent
// connectivity error. We'll allow the ticker to continue.
utils.LogDebug(fmt.Sprintf("Error pinging for liveness \"%s\"", err))
}
}
}
}()

}

startProcess := func() {
// ensure we can fetch the new secrets before restarting the process
secrets := controllers.FetchSecrets(localConfig, enableCache, fallbackOpts, metadataPath, nameTransformer, dynamicSecretsTTL, format, secretsToInclude)
Expand Down Expand Up @@ -366,6 +387,10 @@ doppler run --mount secrets.json -- cat secrets.json`,

startProcess()

if enableLivenessPing {
startLivenessPing()
}

// initiate watch logic after starting the process so that failing to watch just degrades to normal 'run' behavior
if watch {
maxAttempts := 10
Expand Down Expand Up @@ -593,9 +618,10 @@ func init() {
runCmd.Flags().Bool("no-cache", false, "disable using the fallback file to speed up fetches. the fallback file is only used when the API indicates that it's still current.")
runCmd.Flags().Bool("no-fallback", false, "disable reading and writing the fallback file (implies --no-cache)")
runCmd.Flags().Bool("fallback-readonly", false, "disable modifying the fallback file. secrets can still be read from the file.")
runCmd.Flags().Bool("fallback-only", false, "read all secrets directly from the fallback file, without contacting Doppler. secrets will not be updated. (implies --fallback-readonly)")
runCmd.Flags().Bool("fallback-only", false, "read all secrets directly from the fallback file, without contacting Doppler. secrets will not be updated. (implies --fallback-readonly and --no-liveness-ping)")
runCmd.Flags().Bool("no-exit-on-write-failure", false, "do not exit if unable to write the fallback file")
runCmd.Flags().Bool("forward-signals", forwardSignals, "forward signals to the child process (defaults to false when STDOUT is a TTY)")
runCmd.Flags().Bool("no-liveness-ping", false, "disable the periodic liveness ping")
// secrets mount flags
runCmd.Flags().String("mount", "", "write secrets to an ephemeral file, accessible at DOPPLER_CLI_SECRETS_PATH. when enabled, secrets are NOT injected into the environment")
runCmd.Flags().String("mount-format", "json", fmt.Sprintf("file format to use. if not specified, will be auto-detected from mount name. one of %v", models.SecretsMountFormats))
Expand Down
11 changes: 11 additions & 0 deletions pkg/controllers/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,14 @@ func GetEnvironmentIDs(config models.ScopedOptions) ([]string, Error) {
}
return ids, Error{}
}

func LivenessPing(config models.ScopedOptions) (bool, Error) {
utils.RequireValue("token", config.Token.Value)

_, err := http.LivenessPing(config.APIHost.Value, utils.GetBool(config.VerifyTLS.Value, true), config.Token.Value, config.EnclaveProject.Value, config.EnclaveConfig.Value)
if !err.IsNil() {
return false, Error{Err: err.Unwrap(), Message: err.Message}
}

return true, Error{}
}
26 changes: 21 additions & 5 deletions pkg/http/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,11 @@ func RevokeAuthToken(host string, verifyTLS bool, token string) (map[string]inte
return result, Error{}
}


// GetOIDCAuthToken get a short lived service account identity auth token from an OIDC token
func GetOIDCAuthToken(host string, verifyTLS bool, identityId string, oidcJWT string) (map[string]interface{}, Error) {
reqBody := map[string]interface{}{}
reqBody["identity"] = identityId
reqBody["token"] = oidcJWT
reqBody["identity"] = identityId
reqBody["token"] = oidcJWT
body, err := json.Marshal(reqBody)
if err != nil {
return nil, Error{Err: err, Message: "Invalid OIDC auth token"}
Expand All @@ -184,9 +183,8 @@ func GetOIDCAuthToken(host string, verifyTLS bool, identityId string, oidcJWT st
return result, Error{}
}


// RevokeIdentityAuthToken revoke a short lived service account identity auth token
func RevokeIdentityAuthToken(host string, verifyTLS bool, token string) (Error) {
func RevokeIdentityAuthToken(host string, verifyTLS bool, token string) Error {
reqBody := map[string]interface{}{}
reqBody["token"] = token
body, err := json.Marshal(reqBody)
Expand Down Expand Up @@ -911,6 +909,24 @@ func GetConfig(host string, verifyTLS bool, apiKey string, project string, confi
return info, Error{}
}

func LivenessPing(host string, verifyTLS bool, apiKey string, project string, config string) (bool, Error) {
var params []queryParam
params = append(params, queryParam{Key: "project", Value: project})
params = append(params, queryParam{Key: "config", Value: config})

url, err := generateURL(host, "/v3/configs/config/ping", params)
if err != nil {
return false, Error{Err: err, Message: "Unable to generate url"}
}

statusCode, _, _, err := GetRequest(url, verifyTLS, apiKeyHeader(apiKey))
if err != nil {
return false, Error{Err: err, Message: "Unable to liveness ping", Code: statusCode}
}

return true, Error{}
}

// CreateConfig create a config
func CreateConfig(host string, verifyTLS bool, apiKey string, project string, name string, environment string) (models.ConfigInfo, Error) {
postBody := map[string]interface{}{"name": name, "environment": environment}
Expand Down
Loading