From f88230eb4abd5b0b2a5651a06bb877da17518c71 Mon Sep 17 00:00:00 2001 From: jackgopack4 Date: Thu, 19 Sep 2024 13:07:18 -0400 Subject: [PATCH] re-add ocb version command and unit tests --- ...jackgopack4-re-add-ocb-version-number.yaml | 25 ++++ cmd/builder/internal/command.go | 2 +- cmd/builder/internal/version.go | 26 +++- cmd/builder/internal/version_test.go | 123 ++++++++++++++++++ 4 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 .chloggen/jackgopack4-re-add-ocb-version-number.yaml create mode 100644 cmd/builder/internal/version_test.go diff --git a/.chloggen/jackgopack4-re-add-ocb-version-number.yaml b/.chloggen/jackgopack4-re-add-ocb-version-number.yaml new file mode 100644 index 00000000000..1a8bd5595d0 --- /dev/null +++ b/.chloggen/jackgopack4-re-add-ocb-version-number.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: cmd/builder + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: re-adds function to properly set and view version number of OpenTelemetry Collector Builder (ocb) binaries + +# One or more tracking issues or pull requests related to the change +issues: [11208] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/cmd/builder/internal/command.go b/cmd/builder/internal/command.go index 124b51fdc91..b6cd83a1102 100644 --- a/cmd/builder/internal/command.go +++ b/cmd/builder/internal/command.go @@ -116,7 +116,7 @@ configuration is provided, ocb will generate a default Collector. return nil, err } // version of this binary - cmd.AddCommand(versionCommand()) + cmd.AddCommand(versionCommand(binVersion)) return cmd, nil } diff --git a/cmd/builder/internal/version.go b/cmd/builder/internal/version.go index 2e1f39e9f44..886a0483d8f 100644 --- a/cmd/builder/internal/version.go +++ b/cmd/builder/internal/version.go @@ -14,18 +14,34 @@ var ( version = "" ) -func init() { - // the second returned value is a boolean, which is true if the binaries are built with module support. - info, _ := debug.ReadBuildInfo() - version = info.Main.Version +type debugReadBuildInfoFunc func() (info *debug.BuildInfo, ok bool) +type binVersionFunc func(fn debugReadBuildInfoFunc) (string, error) + +// binVersion returns the version of the binary. +// If the version is not set, it attempts to read the build information. +// Returns an error if the build information cannot be read. +func binVersion(fn debugReadBuildInfoFunc) (string, error) { + if version != "" { + return version, nil + } + info, ok := fn() + if !ok { + return "", fmt.Errorf("failed to read build info") + } + return info.Main.Version, nil } -func versionCommand() *cobra.Command { +func versionCommand(fn binVersionFunc) *cobra.Command { + var err error return &cobra.Command{ Use: "version", Short: "Version of ocb", Long: "Prints the version of the ocb binary", RunE: func(cmd *cobra.Command, _ []string) error { + version, err = fn(debug.ReadBuildInfo) + if err != nil { + return err + } cmd.Println(fmt.Sprintf("%s version %s", cmd.Parent().Name(), version)) return nil }, diff --git a/cmd/builder/internal/version_test.go b/cmd/builder/internal/version_test.go new file mode 100644 index 00000000000..8a0c222b09d --- /dev/null +++ b/cmd/builder/internal/version_test.go @@ -0,0 +1,123 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "bytes" + "context" + "fmt" + "os" + "runtime/debug" + "testing" + + "github.com/spf13/cobra" +) + +// Mock debug.ReadBuildInfo function +var readBuildInfo = debug.ReadBuildInfo + +func TestBinVersion(t *testing.T) { + // Test case: version is set + version = "v1.0.0" + v, err := binVersion(readBuildInfo) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if v != "v1.0.0" { + t.Fatalf("expected version 'v1.0.0', got %v", v) + } + + // // Test case: version is not set, ReadBuildInfo returns valid info + version = "" + readBuildInfo = func() (*debug.BuildInfo, bool) { + return &debug.BuildInfo{ + Main: debug.Module{ + Version: "v2.0.0", + }, + }, true + } + v, err = binVersion(readBuildInfo) + fmt.Printf("v: %v, err: %v", v, err) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if v != "v2.0.0" { + t.Fatalf("expected version 'v2.0.0', got %v", v) + } + + // Test case: version is not set, ReadBuildInfo fails + readBuildInfo = func() (*debug.BuildInfo, bool) { + return nil, false + } + v, err = binVersion(readBuildInfo) + if err == nil { + t.Fatalf("expected error, got nil") + } + if v != "" { + t.Fatalf("expected empty version, got %v", v) + } +} + +var validBinVersionFunc binVersionFunc = func(_ debugReadBuildInfoFunc) (string, error) { + return "v1.0.0", nil +} + +var invalidBinVersionFunc binVersionFunc = func(_ debugReadBuildInfoFunc) (string, error) { + return "", fmt.Errorf("failed to get version") +} + +func TestVersionCommand(t *testing.T) { + tests := []struct { + name string + binVersion binVersionFunc + expectedOutput string + expectedError bool + }{ + { + name: "valid version", + binVersion: validBinVersionFunc, + expectedOutput: "ocb version v1.0.0\n", + expectedError: false, + }, + { + name: "error in binVersion", + binVersion: invalidBinVersionFunc, + expectedOutput: "", + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set a mock parent command name + parentCmd := &cobra.Command{ + Use: "ocb ", + } + // Create the command + var cmd = versionCommand(tt.binVersion) + parentCmd.AddCommand(cmd) + // Capture the output + output := bytes.NewBufferString("") + errOutput := bytes.NewBufferString("") + cmd.SetOut(output) + cmd.SetErr(errOutput) + // Create a new context with a fake value + type contextKey string + ctx := context.WithValue(context.Background(), contextKey("key"), "value") + // Set fake CLI arguments + fakeArgs := []string{"cmd", "version"} + os.Args = fakeArgs + // Execute the command + err := cmd.ExecuteContext(ctx) + // Check for expected error + if (err != nil) != tt.expectedError { + t.Fatalf("expected error: %v, got: %v", tt.expectedError, err) + } + // Check for expected output + if output.String() != tt.expectedOutput { + t.Fatalf("expected output: %v, got: %v", tt.expectedOutput, output.String()) + } + }) + } +}