You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by ri...@apache.org on 2023/07/10 21:10:49 UTC
[beam] branch master updated: Implement environment variable type (#27407)
This is an automated email from the ASF dual-hosted git repository.
riteshghorse pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new c5d6183cee1 Implement environment variable type (#27407)
c5d6183cee1 is described below
commit c5d6183cee1c22c572cbf51ec4a2c1490dafafe8
Author: Damon <da...@users.noreply.github.com>
AuthorDate: Mon Jul 10 14:10:43 2023 -0700
Implement environment variable type (#27407)
---
.test-infra/pipelines/go.mod | 21 ++
.test-infra/pipelines/go.sum | 2 +
.../src/main/go/internal/environment/variable.go | 81 +++++
.../main/go/internal/environment/variable_test.go | 358 +++++++++++++++++++++
4 files changed, 462 insertions(+)
diff --git a/.test-infra/pipelines/go.mod b/.test-infra/pipelines/go.mod
new file mode 100644
index 00000000000..0204d037a4c
--- /dev/null
+++ b/.test-infra/pipelines/go.mod
@@ -0,0 +1,21 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This module contains all Go code for internal use by .test-infra pipelines.
+module github.com/apache/beam/test-infra/pipelines
+
+go 1.20
+
+require github.com/google/go-cmp v0.5.9 // indirect
diff --git a/.test-infra/pipelines/go.sum b/.test-infra/pipelines/go.sum
new file mode 100644
index 00000000000..62841cdb151
--- /dev/null
+++ b/.test-infra/pipelines/go.sum
@@ -0,0 +1,2 @@
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
diff --git a/.test-infra/pipelines/src/main/go/internal/environment/variable.go b/.test-infra/pipelines/src/main/go/internal/environment/variable.go
new file mode 100644
index 00000000000..1e81a2e2ce8
--- /dev/null
+++ b/.test-infra/pipelines/src/main/go/internal/environment/variable.go
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package environment provides helpers for interacting with environment variables.
+package environment
+
+import (
+ "fmt"
+ "os"
+ "strings"
+)
+
+// Variable defines an environment variable via a string type alias.
+// Variable's string defaultValue assigns the system environment variable key.
+type Variable string
+
+// Default a defaultValue to the system environment.
+func (v Variable) Default(value string) error {
+ if v.Missing() {
+ return os.Setenv((string)(v), value)
+ }
+ return nil
+}
+
+// Missing reports whether the system environment variable is an empty string.
+func (v Variable) Missing() bool {
+ return v.Value() == ""
+}
+
+// Key returns the system environment variable key.
+func (v Variable) Key() string {
+ return (string)(v)
+}
+
+// Value returns the system environment variable defaultValue.
+func (v Variable) Value() string {
+ return os.Getenv((string)(v))
+}
+
+// KeyValue returns a concatenated string of the system environment variable's
+// <key>=<defaultValue>.
+func (v Variable) KeyValue() string {
+ return fmt.Sprintf("%s=%s", (string)(v), v.Value())
+}
+
+// Missing reports as an error listing all Variable among vars that are
+// not assigned in the system environment.
+func Missing(vars ...Variable) error {
+ var missing []string
+ for _, v := range vars {
+ if v.Missing() {
+ missing = append(missing, v.KeyValue())
+ }
+ }
+ if len(missing) > 0 {
+ return fmt.Errorf("variables empty but expected from environment: %s", strings.Join(missing, "; "))
+ }
+ return nil
+}
+
+// Map converts a slice of Variable into a map.
+// Its usage is for logging purposes.
+func Map(vars ...Variable) map[string]interface{} {
+ result := map[string]interface{}{}
+ for _, v := range vars {
+ result[(string)(v)] = v.Value()
+ }
+ return result
+}
diff --git a/.test-infra/pipelines/src/main/go/internal/environment/variable_test.go b/.test-infra/pipelines/src/main/go/internal/environment/variable_test.go
new file mode 100644
index 00000000000..6675b39ab26
--- /dev/null
+++ b/.test-infra/pipelines/src/main/go/internal/environment/variable_test.go
@@ -0,0 +1,358 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package environment
+
+import (
+ "errors"
+ "github.com/google/go-cmp/cmp"
+ "os"
+ "testing"
+)
+
+func TestMap(t *testing.T) {
+ type args struct {
+ vars []Variable
+ values []string
+ }
+ tests := []struct {
+ name string
+ args args
+ want map[string]interface{}
+ }{
+ {
+ name: "{}",
+ args: args{},
+ want: map[string]interface{}{},
+ },
+ {
+ name: "{A=1; B=2; C=3}",
+ args: args{
+ vars: []Variable{
+ "A",
+ "B",
+ "C",
+ },
+ values: []string{
+ "1",
+ "2",
+ "3",
+ },
+ },
+ want: map[string]interface{}{
+ "A": "1",
+ "B": "2",
+ "C": "3",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ clear(tt.args.vars...)
+ set(t, tt.args.vars, tt.args.values)
+ got := Map(tt.args.vars...)
+ if diff := cmp.Diff(got, tt.want); diff != "" {
+ t.Errorf("Map() = %v, want %v, diff:\n%v", got, tt.want, diff)
+ }
+ })
+ }
+}
+
+func TestMissing(t *testing.T) {
+ type args struct {
+ vars []Variable
+ values []string
+ }
+ tests := []struct {
+ name string
+ args args
+ want error
+ }{
+ {
+ name: "{}",
+ args: args{},
+ },
+ {
+ name: "{A=}",
+ args: args{
+ vars: []Variable{
+ "A",
+ },
+ values: []string{
+ "",
+ },
+ },
+ want: errors.New("variables empty but expected from environment: A="),
+ },
+ {
+ name: "{A=1}",
+ args: args{
+ vars: []Variable{
+ "A",
+ },
+ values: []string{
+ "1",
+ },
+ },
+ want: nil,
+ },
+ {
+ name: "{A=; B=}",
+ args: args{
+ vars: []Variable{
+ "A",
+ "B",
+ },
+ values: []string{
+ "",
+ "",
+ },
+ },
+ want: errors.New("variables empty but expected from environment: A=; B="),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var got, want string
+ clear(tt.args.vars...)
+ set(t, tt.args.vars, tt.args.values)
+ err := Missing(tt.args.vars...)
+ if err != nil {
+ got = err.Error()
+ }
+ if tt.want != nil {
+ want = tt.want.Error()
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Missing() error = %v, want %v, diff:\n%s", err, tt.want, diff)
+ }
+ })
+ }
+}
+
+func TestVariable_Default(t *testing.T) {
+ type args struct {
+ setValue string
+ defaultValue string
+ }
+ tests := []struct {
+ name string
+ v Variable
+ args args
+ want string
+ }{
+ {
+ name: "environment variable not set",
+ v: "A",
+ args: args{
+ defaultValue: "1",
+ },
+ want: "1",
+ },
+ {
+ name: "environment variable default is overridden by set value",
+ v: "A",
+ args: args{
+ setValue: "2",
+ defaultValue: "1",
+ },
+ want: "2",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ clear(tt.v)
+ if tt.args.setValue != "" {
+ set(t, []Variable{tt.v}, []string{tt.args.setValue})
+ }
+ if err := tt.v.Default(tt.args.defaultValue); err != nil {
+ t.Fatalf("could not set default environment variable value during test execution: %v", err)
+ }
+ got := os.Getenv(tt.v.Key())
+ if diff := cmp.Diff(got, tt.want); diff != "" {
+ t.Errorf("Default() = %s, want %s, diff:\n%s", got, tt.want, diff)
+ }
+ })
+ }
+}
+
+func TestVariable_KeyValue(t *testing.T) {
+ tests := []struct {
+ name string
+ v Variable
+ value string
+ want string
+ }{
+ {
+ name: "environment variable not set",
+ v: "A",
+ want: "A=",
+ },
+ {
+ name: "environment variable is set",
+ v: "A",
+ value: "1",
+ want: "A=1",
+ },
+ }
+ for _, tt := range tests {
+ clear(tt.v)
+ t.Run(tt.name, func(t *testing.T) {
+ set(t, []Variable{tt.v}, []string{tt.value})
+ got := tt.v.KeyValue()
+ if diff := cmp.Diff(got, tt.want); diff != "" {
+ t.Errorf("KeyValue() = %v, want %v, diff:\n%s", got, tt.want, diff)
+ }
+ })
+ }
+}
+
+func TestVariable_Missing(t *testing.T) {
+ type args struct {
+ setValue string
+ defaultValue string
+ }
+ tests := []struct {
+ name string
+ args args
+ v Variable
+ want bool
+ }{
+ {
+ name: "no default and not set",
+ args: args{},
+ v: "A",
+ want: true,
+ },
+ {
+ name: "has default but not set",
+ args: args{
+ defaultValue: "1",
+ },
+ v: "A",
+ want: false,
+ },
+ {
+ name: "no default but set",
+ args: args{
+ setValue: "1",
+ },
+ v: "A",
+ want: false,
+ },
+ {
+ name: "has default and set",
+ args: args{
+ setValue: "2",
+ defaultValue: "1",
+ },
+ v: "A",
+ want: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ clear(tt.v)
+ if tt.args.defaultValue != "" {
+ if err := tt.v.Default(tt.args.defaultValue); err != nil {
+ t.Fatalf("could not set default environment variable value during test execution: %v", err)
+ }
+ }
+ if tt.args.setValue != "" {
+ set(t, []Variable{tt.v}, []string{tt.args.setValue})
+ }
+ if got := tt.v.Missing(); got != tt.want {
+ t.Errorf("Missing() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestVariable_Value(t *testing.T) {
+ type args struct {
+ setValue string
+ defaultValue string
+ }
+ tests := []struct {
+ name string
+ args args
+ v Variable
+ want string
+ }{
+ {
+ name: "no default and not set",
+ args: args{},
+ v: "A",
+ want: "",
+ },
+ {
+ name: "has default but not set",
+ args: args{
+ defaultValue: "1",
+ },
+ v: "A",
+ want: "1",
+ },
+ {
+ name: "no default but set",
+ args: args{
+ setValue: "1",
+ },
+ v: "A",
+ want: "1",
+ },
+ {
+ name: "has default and set",
+ args: args{
+ setValue: "2",
+ defaultValue: "1",
+ },
+ v: "A",
+ want: "2",
+ },
+ }
+ for _, tt := range tests {
+ clear(tt.v)
+ if tt.args.defaultValue != "" {
+ if err := tt.v.Default(tt.args.defaultValue); err != nil {
+ t.Fatalf("could not set default environment variable value during test execution: %v", err)
+ }
+ }
+ if tt.args.setValue != "" {
+ set(t, []Variable{tt.v}, []string{tt.args.setValue})
+ }
+ t.Run(tt.name, func(t *testing.T) {
+ if got := tt.v.Value(); got != tt.want {
+ t.Errorf("Value() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func clear(vars ...Variable) {
+ for _, k := range vars {
+ _ = os.Setenv(k.Key(), "")
+ }
+}
+
+func set(t *testing.T, vars []Variable, values []string) {
+ if len(vars) != len(values) {
+ t.Fatalf("test cases should be configured with matching args.vars and args.values: len(tt.args.vars): %v != len(tt.args.values): %v", len(vars), len(values))
+ }
+ for i := range vars {
+ key := vars[i].Key()
+ value := values[i]
+ _ = os.Setenv(key, value)
+ }
+}