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
20 changes: 20 additions & 0 deletions pkg/envmap/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright The CloudNativePG Contributors

Licensed 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 envmap is a set of utility functions to manage a process
// environment as a map between the environment variable names and their
// value
package envmap
91 changes: 91 additions & 0 deletions pkg/envmap/envutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Copyright The CloudNativePG Contributors

Licensed 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 envmap

import (
"fmt"
"maps"
"os"
"strings"

"github.com/cloudnative-pg/machinery/pkg/stringset"
)

// ErrWrongEnvironmentString is raised when the Parse function detects
// a wrong environment string.
// By convention, the strings in environment should have the form
// "name=value".
//
// See: https://www.man7.org/linux/man-pages/man7/environ.7.html
type ErrWrongEnvironmentString struct {
entry string
}

// Error implements the error interface.
func (e *ErrWrongEnvironmentString) Error() string {
return fmt.Sprintf("wrong environment variable entry: %s", e.entry)
}

// EnvironmentMap represent a map between environment variable names
// and their value.
type EnvironmentMap map[string]string

// Parse parses a list of strings in the form "foo=bar" into an environment map.
// If an envalid string is detected, this function will return a
// ErrWrongEnvironmentString error for the invalid entry.
func Parse(env []string) (EnvironmentMap, error) {
result := make(map[string]string, len(env))

for _, entry := range env {
prefix, suffix, found := strings.Cut(entry, "=")
if !found {
return nil, &ErrWrongEnvironmentString{
entry: entry,
}
}

result[prefix] = suffix
}

return result, nil
}

// StringSlice converts an environment map to a list of strings in the form "foo=bar".
// The returned list is sorted in lexicographic key order.
func (e EnvironmentMap) StringSlice() []string {
keys := stringset.FromKeys(e).ToSortedList()
result := make([]string, len(keys))
for i, keyName := range keys {
result[i] = fmt.Sprintf("%s=%s", keyName, e[keyName])
}
return result
}

// ParseEnviron returns the environment of the current process.
func ParseEnviron() (EnvironmentMap, error) {
return Parse(os.Environ())
}

// Merge merges two environment maps.
// When a key in e1 is also present in e2,
// the value associated with the key in e2 will be used.
func Merge(e1, e2 EnvironmentMap) EnvironmentMap {
result := make(EnvironmentMap, len(e1)+len(e2))
maps.Copy(result, e1)
maps.Copy(result, e2)
return result
}
185 changes: 185 additions & 0 deletions pkg/envmap/envutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
Copyright The CloudNativePG Contributors

Licensed 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 envmap

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Environment map", func() {
DescribeTable(
"Environment map parsing",
func(env []string, expectedResult EnvironmentMap, expectedError error) {
result, err := Parse(env)
if expectedError == nil {
Expect(err).ToNot(HaveOccurred())
} else {
Expect(err).To(Equal(expectedError))
}
Expect(result).To(Equal(expectedResult))
},
Entry(
"nil slice",
nil,
map[string]string{},
nil,
),
Entry(
"basic test",
[]string{
"PATH=/usr/local/bin:/usr/bin",
"TERM=xterm-256color",
},
map[string]string{
"PATH": "/usr/local/bin:/usr/bin",
"TERM": "xterm-256color",
},
nil,
),
Entry(
"duplicate key",
[]string{
"PATH=/usr/local/bin:/usr/bin",
"PATH=/usr/bin",
},
map[string]string{
"PATH": "/usr/bin",
},
nil,
),
Entry(
"wrong entry (too many equal signs)",
[]string{
"PATH=/usr/local/bin:/usr/bin",
"TERM=xterm-256color=boh",
},
map[string]string{
"PATH": "/usr/local/bin:/usr/bin",
"TERM": "xterm-256color=boh",
},
nil,
),
Entry(
"wrong entry (no equal sign)",
[]string{
"PATH=/usr/local/bin:/usr/bin",
"TERM",
},
nil,
&ErrWrongEnvironmentString{
entry: "TERM",
},
),
)

It("parses the current environment", func() {
envMap, err := ParseEnviron()
Expect(err).ToNot(HaveOccurred())
Expect(envMap).ToNot(BeNil())
})

DescribeTable(
"Environment map merging",
func(e1, e2, result EnvironmentMap) {
Expect(Merge(e1, e2)).To(Equal(result))
},
Entry(
"nil arguments",
nil,
nil,
map[string]string{},
),
Entry(
"e1 is nil",
nil,
map[string]string{
"PATH": "/usr/local/bin",
},
map[string]string{
"PATH": "/usr/local/bin",
},
),
Entry(
"e2 is nil",
map[string]string{
"PATH": "/usr/local/bin",
},
nil,
map[string]string{
"PATH": "/usr/local/bin",
},
),
Entry(
"e1 and e2 have no common keys",
map[string]string{
"PATH": "/usr/local/bin",
},
map[string]string{
"TERM": "xterm-256color",
},
map[string]string{
"PATH": "/usr/local/bin",
"TERM": "xterm-256color",
},
),
Entry(
"e1 and e2 have common keys, e2 will override e1",
map[string]string{
"PATH": "/usr/local/bin",
"TERM": "xterm-256color",
},
map[string]string{
"PATH": "/etc",
},
map[string]string{
"PATH": "/etc",
"TERM": "xterm-256color",
},
),
)

DescribeTable(
"Environment map StringSlice",
func(e EnvironmentMap, expectedResult []string) {
Expect(e.StringSlice()).To(Equal(expectedResult))
},
Entry(
"nil environment map",
nil,
[]string{},
),
Entry(
"empty environment map",
map[string]string{},
[]string{},
),
Entry(
"environment map sorted in lexicographical order",
map[string]string{
"TERM": "xterm-256color",
"PATH": "/usr/local/bin",
"GPG_TTY": "/dev/ttys008",
},
[]string{
"GPG_TTY=/dev/ttys008",
"PATH=/usr/local/bin",
"TERM=xterm-256color",
},
),
)
})
33 changes: 33 additions & 0 deletions pkg/envmap/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright The CloudNativePG Contributors

Licensed 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 envmap

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.

func TestEnvUtils(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecs(t, "Environment variables utils test")
}