Skip to content

Commit

Permalink
feat(cloudruntime): add Cloud Run Jobs config
Browse files Browse the repository at this point in the history
  • Loading branch information
odsod committed Jul 5, 2023
1 parent 23efe7f commit cc73c52
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ cloudrunner PORT int
cloudrunner K_SERVICE string
cloudrunner K_REVISION string
cloudrunner K_CONFIGURATION string
cloudrunner CLOUD_RUN_JOB string
cloudrunner CLOUD_RUN_EXECUTION string
cloudrunner CLOUD_RUN_TASK_INDEX int
cloudrunner CLOUD_RUN_TASK_ATTEMPT int
cloudrunner CLOUD_RUN_TASK_COUNT int
cloudrunner GOOGLE_CLOUD_PROJECT string
cloudrunner RUNTIME_SERVICEACCOUNT string
cloudrunner SERVICE_VERSION string
Expand Down
25 changes: 25 additions & 0 deletions cloudruntime/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ type Config struct {
Revision string `env:"K_REVISION"`
// Configuration of the service, as assigned by a Knative runtime.
Configuration string `env:"K_CONFIGURATION"`
// Job name, if running as a Cloud Run job.
Job string `env:"CLOUD_RUN_JOB"`
// Execution name, if running as a Cloud Run job.
Execution string `env:"CLOUD_RUN_EXECUTION"`
// TaskIndex of the current task, if running as a Cloud Run job.
TaskIndex int `env:"CLOUD_RUN_TASK_INDEX"`
// TaskAttempt of the current task, if running as a Cloud Run job.
TaskAttempt int `env:"CLOUD_RUN_TASK_ATTEMPT"`
// TaskCount of the job, if running as a Cloud Run job.
TaskCount int `env:"CLOUD_RUN_TASK_COUNT"`
// ProjectID is the GCP project ID the service is running in.
// In production, defaults to the project where the service is deployed.
ProjectID string `env:"GOOGLE_CLOUD_PROJECT"`
Expand Down Expand Up @@ -55,5 +65,20 @@ func (c *Config) Autodetect() error {
if configuration, ok := Configuration(); ok {
c.Configuration = configuration
}
if job, ok := Job(); ok {
c.Job = job
}
if execution, ok := Execution(); ok {
c.Execution = execution
}
if taskIndex, ok := TaskIndex(); ok {
c.TaskIndex = taskIndex
}
if taskAttempt, ok := TaskAttempt(); ok {
c.TaskAttempt = taskAttempt
}
if taskCount, ok := TaskCount(); ok {
c.TaskCount = taskCount
}
return nil
}
32 changes: 32 additions & 0 deletions cloudruntime/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,35 @@ func Port() (int, bool) {
port, err := strconv.Atoi(os.Getenv("PORT"))
return port, err == nil
}

// Job returns the name of the Cloud Run job being run.
func Job() (string, bool) {
return os.LookupEnv("CLOUD_RUN_JOB")
}

// Execution returns the name of the Cloud Run job execution being run.
func Execution() (string, bool) {
return os.LookupEnv("CLOUD_RUN_EXECUTION")
}

// TaskIndex returns the index of the Cloud Run job task being run.
// Starts at 0 for the first task and increments by 1 for every successive task,
// up to the maximum number of tasks minus 1.
func TaskIndex() (int, bool) {
taskIndex, err := strconv.Atoi(os.Getenv("CLOUD_RUN_TASK_INDEX"))
return taskIndex, err == nil
}

// TaskAttempt returns the number of time this Cloud Run job task tas been retried.
// Starts at 0 for the first attempt and increments by 1 for every successive retry,
// up to the maximum retries value.
func TaskAttempt() (int, bool) {
taskIndex, err := strconv.Atoi(os.Getenv("CLOUD_RUN_TASK_ATTEMPT"))
return taskIndex, err == nil
}

// TaskCount returns the number of tasks in the current Cloud Run job.
func TaskCount() (int, bool) {
taskIndex, err := strconv.Atoi(os.Getenv("CLOUD_RUN_TASK_COUNT"))
return taskIndex, err == nil
}
101 changes: 101 additions & 0 deletions cloudruntime/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,107 @@ func TestPort(t *testing.T) {
})
}

func TestJob(t *testing.T) {
t.Run("from env", func(t *testing.T) {
const expected = "foo"
setEnv(t, "CLOUD_RUN_JOB", expected)
actual, ok := Job()
assert.Assert(t, ok)
assert.Equal(t, expected, actual)
})

t.Run("undefined", func(t *testing.T) {
actual, ok := Job()
assert.Assert(t, !ok)
assert.Equal(t, "", actual)
})
}

func TestExecution(t *testing.T) {
t.Run("from env", func(t *testing.T) {
const expected = "foo"
setEnv(t, "CLOUD_RUN_EXECUTION", expected)
actual, ok := Execution()
assert.Assert(t, ok)
assert.Equal(t, expected, actual)
})

t.Run("undefined", func(t *testing.T) {
actual, ok := Execution()
assert.Assert(t, !ok)
assert.Equal(t, "", actual)
})
}

func TestTaskIndex(t *testing.T) {
t.Run("from env", func(t *testing.T) {
const expected = 42
setEnv(t, "CLOUD_RUN_TASK_INDEX", strconv.Itoa(expected))
actual, ok := TaskIndex()
assert.Assert(t, ok)
assert.Equal(t, expected, actual)
})

t.Run("invalid", func(t *testing.T) {
setEnv(t, "CLOUD_RUN_TASK_INDEX", "invalid")
actual, ok := TaskIndex()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})

t.Run("undefined", func(t *testing.T) {
actual, ok := TaskIndex()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})
}

func TestTaskAttempt(t *testing.T) {
t.Run("from env", func(t *testing.T) {
const expected = 42
setEnv(t, "CLOUD_RUN_TASK_ATTEMPT", strconv.Itoa(expected))
actual, ok := TaskAttempt()
assert.Assert(t, ok)
assert.Equal(t, expected, actual)
})

t.Run("invalid", func(t *testing.T) {
setEnv(t, "CLOUD_RUN_TASK_ATTEMPT", "invalid")
actual, ok := TaskAttempt()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})

t.Run("undefined", func(t *testing.T) {
actual, ok := TaskAttempt()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})
}

func TestTaskCount(t *testing.T) {
t.Run("from env", func(t *testing.T) {
const expected = 42
setEnv(t, "CLOUD_RUN_TASK_COUNT", strconv.Itoa(expected))
actual, ok := TaskCount()
assert.Assert(t, ok)
assert.Equal(t, expected, actual)
})

t.Run("invalid", func(t *testing.T) {
setEnv(t, "CLOUD_RUN_TASK_COUNT", "invalid")
actual, ok := TaskCount()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})

t.Run("undefined", func(t *testing.T) {
actual, ok := TaskCount()
assert.Assert(t, !ok)
assert.Equal(t, 0, actual)
})
}

// setEnv will be available in the standard library from Go 1.17 as t.SetEnv.
func setEnv(t *testing.T, key, value string) {
prevValue, ok := os.LookupEnv(key)
Expand Down

0 comments on commit cc73c52

Please sign in to comment.