diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c0290b65..7bedaec2 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,11 +20,12 @@ jobs: - uses: actions/checkout@v3 - uses: Jerome1337/gofmt-action@v1.0.5 tests: - runs-on: ubuntu-latest strategy: matrix: goversion: ['1.21', '1.22'] - name: Go ${{ matrix.goversion }} tests + os: ['ubuntu-latest', 'windows-latest'] + name: Go ${{ matrix.goversion }} (${{ matrix.os}}) tests + runs-on: ${{ matrix.os }} env: SW_APM_DEBUG_LEVEL: 1 steps: diff --git a/internal/config/config_test.go b/internal/config/config_test.go index e8b0f7cf..3347ba4b 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -18,6 +18,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strings" "testing" @@ -342,14 +343,20 @@ func TestYamlConfig(t *testing.T) { } out, err := yaml.Marshal(&yamlConfig) - assert.Nil(t, err) + require.NoError(t, err) - err = os.WriteFile("/tmp/solarwinds-apm-config.yaml", out, 0644) - assert.Nil(t, err) + f, err := os.CreateTemp("", "*-test-config.yaml") + require.NoError(t, err) + defer func() { + _ = f.Close() + os.Remove(f.Name()) + }() + err = os.WriteFile(f.Name(), out, 0644) + require.NoError(t, err) // Test with config file ClearEnvs() - os.Setenv(envSolarWindsAPMConfigFile, "/tmp/solarwinds-apm-config.yaml") + os.Setenv(envSolarWindsAPMConfigFile, f.Name()) c := NewConfig() assert.Equal(t, &yamlConfig, c) @@ -377,7 +384,7 @@ func TestYamlConfig(t *testing.T) { } ClearEnvs() SetEnvs(envs) - os.Setenv("SW_APM_CONFIG_FILE", "/tmp/solarwinds-apm-config.yaml") + os.Setenv("SW_APM_CONFIG_FILE", f.Name()) envConfig := Config{ Collector: "collector.test.com", @@ -458,11 +465,17 @@ func TestInvalidConfigFile(t *testing.T) { log.SetOutput(os.Stderr) log.SetLevel(oldLevel) }() + f, err := os.CreateTemp("", "*-test-config.json") + require.NoError(t, err) + defer func() { + _ = f.Close() + os.Remove(f.Name()) + }() ClearEnvs() os.Setenv("SW_APM_SERVICE_KEY", "ae38315f6116585d64d82ec2455aa3ec61e02fee25d286f74ace9e4fea189217:go") - os.Setenv("SW_APM_CONFIG_FILE", "/tmp/solarwinds-apm-config.json") - require.NoError(t, os.WriteFile("/tmp/solarwinds-apm-config.json", []byte("hello"), 0644)) + os.Setenv("SW_APM_CONFIG_FILE", f.Name()) + require.NoError(t, os.WriteFile(f.Name(), []byte("hello"), 0644)) _ = NewConfig() assert.Contains(t, buf.String(), ErrUnsupportedFormat.Error()) @@ -474,7 +487,13 @@ func TestInvalidConfigFile(t *testing.T) { os.Setenv("SW_APM_SERVICE_KEY", "ae38315f6116585d64d82ec2455aa3ec61e02fee25d286f74ace9e4fea189217:go") os.Setenv("SW_APM_CONFIG_FILE", "/tmp/file-not-exist.yaml") _ = NewConfig() - assert.Contains(t, buf.String(), "no such file or directory") + var exp string + if runtime.GOOS == "windows" { + exp = "The system cannot find the path specified." + } else { + exp = "no such file or directory" + } + assert.Contains(t, buf.String(), exp) } func TestInvalidConfig(t *testing.T) { diff --git a/internal/host/azure/azure_test.go b/internal/host/azure/azure_test.go index 28f95674..05b239b1 100644 --- a/internal/host/azure/azure_test.go +++ b/internal/host/azure/azure_test.go @@ -15,6 +15,7 @@ package azure import ( + "errors" "github.com/solarwinds/apm-go/internal/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,10 +41,10 @@ func TestQueryAzureInvalidJson(t *testing.T) { func TestQueryAzureNoHttpResponse(t *testing.T) { m, err := queryAzureIMDS("http://127.0.0.1:12345/asdf") - assert.Error(t, err) - assert.Nil(t, m) - assert.Equal(t, `Get "http://127.0.0.1:12345/asdf?api-version=2021-12-13&format=json": dial tcp 127.0.0.1:12345: connect: connection refused`, err.Error()) - assert.Nil(t, m.ToPB()) + require.Error(t, err) + require.Nil(t, m) + require.True(t, errors.Is(err, testutils.ConnectionRefusedError), "%+v", err) + require.Nil(t, m.ToPB()) } func TestQueryAzureIMDS(t *testing.T) { diff --git a/internal/host/k8s/k8s.go b/internal/host/k8s/k8s.go index 49978386..f92c2ff8 100644 --- a/internal/host/k8s/k8s.go +++ b/internal/host/k8s/k8s.go @@ -172,6 +172,11 @@ func getPodUidFromProc(fn string) (string, error) { if err != nil { return "", err } + defer func() { + if err := f.Close(); err != nil { + log.Warningf("Could not close file: %s", err) + } + }() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() diff --git a/internal/host/k8s/k8s_test.go b/internal/host/k8s/k8s_test.go index 20cd531f..da3db919 100644 --- a/internal/host/k8s/k8s_test.go +++ b/internal/host/k8s/k8s_test.go @@ -96,7 +96,7 @@ func TestRequestMetadataFromEnv(t *testing.T) { md, err := requestMetadata() require.Error(t, err) require.Nil(t, md) - require.Equal(t, "open /run/secrets/kubernetes.io/serviceaccount/namespace: no such file or directory", err.Error()) + require.True(t, os.IsNotExist(err)) defer testutils.Setenv(t, "SW_K8S_POD_NAMESPACE", "my env namespace")() md, err = requestMetadata() @@ -124,7 +124,7 @@ func TestRequestMetadataNoNamespace(t *testing.T) { md, err := requestMetadata() require.Error(t, err) require.Nil(t, md) - require.Equal(t, fmt.Sprintf("open %s: no such file or directory", determineNamspaceFileForOS()), err.Error()) + require.True(t, os.IsNotExist(err)) } func TestMetadata_ToPB(t *testing.T) { @@ -159,9 +159,8 @@ func TestGetNamespaceNoneFound(t *testing.T) { require.NoError(t, os.Unsetenv("SW_K8S_POD_NAMESPACE")) ns, err := getNamespaceFromFile("this file does not exist and should not be opened") require.Error(t, err) - require.Equal(t, "open this file does not exist and should not be opened: no such file or directory", err.Error()) + require.True(t, os.IsNotExist(err)) require.Equal(t, "", ns) - } // Test getPodName diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go index a495c371..a471e388 100644 --- a/internal/oboe/settings_lambda_test.go +++ b/internal/oboe/settings_lambda_test.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !windows + package oboe import ( diff --git a/internal/reporter/certgen.sh b/internal/reporter/certgen.sh index c6b8409a..2c6bff18 100755 --- a/internal/reporter/certgen.sh +++ b/internal/reporter/certgen.sh @@ -14,11 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +CONFIG=$(mktemp) +echo "[req] +distinguished_name=req_distinguished_name +prompt=no +[san] +subjectAltName=DNS:localhost +[req_distinguished_name] +CN=localhost +" > $CONFIG +echo $CONFIG openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \ - -keyout for_test.key -out for_test.crt -extensions san -config \ - <(echo "[req]"; - echo distinguished_name=req; - echo "[san]"; - echo subjectAltName=DNS:localhost - ) \ - -subj "/CN=localhost" + -keyout for_test.key -out for_test.crt -extensions san -config "$CONFIG" +rm $CONFIG \ No newline at end of file diff --git a/internal/reporter/reporter_grpc_test.go b/internal/reporter/reporter_grpc_test.go index 198d6865..b1fc68de 100644 --- a/internal/reporter/reporter_grpc_test.go +++ b/internal/reporter/reporter_grpc_test.go @@ -12,6 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +// There's an issue in GitHub Actions running Windows that causes `net.Listen` +// to fail with: +// +// listen tcp: lookup localhost: getaddrinfow: A non-recoverable error +// occurred during a database lookup. +// +// For now, we're going to rely on non-windows to verify that this code works. +// Over time, the (non-OTLP) grpc implementation will be phased out, so I think +// this is a reasonable compromise. +// -- @swi-jared + +//go:build !windows + package reporter import ( diff --git a/internal/reporter/reporter_test.go b/internal/reporter/reporter_test.go index f3c1548d..eeed2a2d 100644 --- a/internal/reporter/reporter_test.go +++ b/internal/reporter/reporter_test.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// See note in `reporter_grpc_test.go` +//go:build !windows + package reporter import ( diff --git a/internal/testutils/testutils_unix.go b/internal/testutils/testutils_unix.go new file mode 100644 index 00000000..8b95de0b --- /dev/null +++ b/internal/testutils/testutils_unix.go @@ -0,0 +1,21 @@ +// © 2023 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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. + +//go:build !windows + +package testutils + +import "syscall" + +const ConnectionRefusedError = syscall.ECONNREFUSED diff --git a/internal/testutils/testutils_windows.go b/internal/testutils/testutils_windows.go new file mode 100644 index 00000000..5f8fd6c5 --- /dev/null +++ b/internal/testutils/testutils_windows.go @@ -0,0 +1,21 @@ +// © 2023 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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. + +//go:build windows + +package testutils + +import "golang.org/x/sys/windows" + +const ConnectionRefusedError = windows.WSAECONNREFUSED diff --git a/internal/uams/file_test.go b/internal/uams/file_test.go index 0c4d9fa5..fe2d8f83 100644 --- a/internal/uams/file_test.go +++ b/internal/uams/file_test.go @@ -15,9 +15,11 @@ package uams import ( + "errors" "fmt" "github.com/google/uuid" "github.com/stretchr/testify/require" + "io/fs" "os" "testing" ) @@ -29,7 +31,7 @@ func TestReadFromFileNoExists(t *testing.T) { uid, err := ReadFromFile(testfile) require.Equal(t, uuid.Nil, uid) require.Error(t, err) - require.Equal(t, "could not stat uams client file: stat /tmp/foobarbaz: no such file or directory", err.Error()) + require.True(t, errors.Is(err, fs.ErrNotExist)) } func TestReadFromFileDirectory(t *testing.T) { diff --git a/internal/uams/http_test.go b/internal/uams/http_test.go index e74c2dcf..54142a71 100644 --- a/internal/uams/http_test.go +++ b/internal/uams/http_test.go @@ -15,6 +15,7 @@ package uams import ( + "errors" "fmt" "github.com/google/uuid" "github.com/solarwinds/apm-go/internal/testutils" @@ -27,7 +28,7 @@ func TestReadFromHttpConnRefused(t *testing.T) { uid, err := ReadFromHttp("http://127.0.0.1:12345") require.Error(t, err) require.Equal(t, uuid.Nil, uid) - require.Equal(t, `Get "http://127.0.0.1:12345": dial tcp 127.0.0.1:12345: connect: connection refused`, err.Error()) + require.True(t, errors.Is(err, testutils.ConnectionRefusedError), "%+v", err) } func TestReadFromHttp(t *testing.T) { diff --git a/internal/uams/uams_test.go b/internal/uams/uams_test.go index 353d6e56..063b8b11 100644 --- a/internal/uams/uams_test.go +++ b/internal/uams/uams_test.go @@ -22,6 +22,7 @@ import ( "net/http" "os" "path/filepath" + "runtime" "testing" "time" ) @@ -38,7 +39,15 @@ func TestUpdateClientId(t *testing.T) { uid, err := uuid.NewRandom() require.NoError(t, err) a := time.Now() + // windows doesn't seem to notice that a few nanoseconds have passed, so we + // introduce a touch of delay + if runtime.GOOS == "windows" { + time.Sleep(time.Millisecond) + } updateClientId(uid, "file") + if runtime.GOOS == "windows" { + time.Sleep(time.Millisecond) + } b := time.Now() require.Equal(t, uid, currState.clientId) require.Equal(t, "file", currState.via)