Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
38 changes: 38 additions & 0 deletions cmd/nrdot-collector-builder/cmd/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2025 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd

import (
"fmt"
"newrelic-collector-builder/cmd/manifest"

"github.com/spf13/cobra"
)

var manifestConfigPath string

// manifestCmd represents the manifest command
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder to align on our team's 'philosophy' on comments

var manifestCmd = &cobra.Command{
Use: "manifest",
Short: "Manage the OCB manifest file",
Long: `
The manifest command allows you to manage the OCB manifest file.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("manifest called")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional or is this a leftover debugging artifact?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's leftover from the cobra scaffolding

},
}

func init() {
rootCmd.AddCommand(manifestCmd)
// Register the update subcommand
manifestCmd.AddCommand(manifest.UpdateCmd)

// Define a persistent flag for `manifestCmd`
manifestCmd.PersistentFlags().StringVarP(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When testing, I absent-mindedly provided the config.yaml instead of the manifest.yaml and it obviously failed. As we have two configs in the collector world and the runtime configs are the ones that usually get more attention and are thus more associated with the 'config' word, we might want to think about using a different name/flag here? Just a thought but wanted to mention it.

&manifestConfigPath,
"config",
"c",
"",
"Path to the manifest configuration file",
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This is an invalid config file for testing purposes. It should not be used in production.
#

dist:
module: github.com/newrelic/nrdot-collector-releases/nrdot-collector-host
name: nrdot-collector-host
description: NRDOT Collector Host
version: 1.1.0
output_path: ./_build
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
dist:
module: go.opentelemetry.io/collector/cmd/otelcorecol
name: otelcorecol
description: Local OpenTelemetry Collector binary, testing only.
version: 0.125.0-dev

receivers:
- gomod: go.opentelemetry.io/collector/receiver/nopreceiver v0.125.0
- gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.125.0
exporters:
- gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.125.0
- gomod: go.opentelemetry.io/collector/exporter/nopexporter v0.125.0
- gomod: go.opentelemetry.io/collector/exporter/otlpexporter v0.125.0
- gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.125.0
extensions:
- gomod: go.opentelemetry.io/collector/extension/memorylimiterextension v0.125.0
- gomod: go.opentelemetry.io/collector/extension/zpagesextension v0.125.0
processors:
- gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.125.0
- gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.125.0
connectors:
- gomod: go.opentelemetry.io/collector/connector/forwardconnector v0.125.0

providers:
- gomod: go.opentelemetry.io/collector/confmap/provider/envprovider v1.31.0
- gomod: go.opentelemetry.io/collector/confmap/provider/fileprovider v1.31.0
- gomod: go.opentelemetry.io/collector/confmap/provider/httpprovider v1.31.0
- gomod: go.opentelemetry.io/collector/confmap/provider/httpsprovider v1.31.0
- gomod: go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.31.0

98 changes: 98 additions & 0 deletions cmd/nrdot-collector-builder/cmd/manifest/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2025 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package manifest

import (
"fmt"
"newrelic-collector-builder/internal/manifest"
"path/filepath"

"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/v2"

"github.com/knadh/koanf/providers/file"
"github.com/spf13/cobra"
"go.uber.org/zap"
)

// UpdateCmd represents the `manifest update` subcommand
var UpdateCmd = &cobra.Command{
Use: "update",
Short: "Update the manifest file",
Long: "Update the manifest file to ensure otel components are up to date.",

RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("manifest update called")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug artifact?

configPath, _ := cmd.Flags().GetString("config")

matches, _ := filepath.Glob(configPath)

if len(matches) == 0 {
fmt.Println("No files matched the pattern.")
return nil
}

for _, match := range matches {
cfg, _, err := initConfig(match)

if err != nil {
return err
}

if err = cfg.Validate(); err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}

if err = cfg.SetGoPath(); err != nil {
return fmt.Errorf("go not found: %w", err)
}

if err = cfg.ParseModules(); err != nil {
return fmt.Errorf("invalid module configuration: %w", err)
}

updatedCfg, err := manifest.UpdateConfigModules(cfg)
if err != nil {
return fmt.Errorf("failed to update configuration: %w", err)
}

if err = manifest.WriteConfigFile(updatedCfg); err != nil {
return fmt.Errorf("failed to write configuration file: %w", err)
}

}
return nil

},
}

func initConfig(cfgFile string) (*manifest.Config, *koanf.Koanf, error) {
var err error
log, err := zap.NewDevelopment()
if err != nil {
return nil, nil, fmt.Errorf("failed to create logger: %w", err)
}

cfg := &manifest.Config{
Logger: log,
}

cfg.Logger.Info("Using config file", zap.String("path", cfgFile))
// load the config file
provider := file.Provider(cfgFile)

k := koanf.New(".")

if err = k.Load(provider, yaml.Parser()); err != nil {
return nil, nil, fmt.Errorf("failed to load configuration file: %w", err)
}

if err = k.UnmarshalWithConf("", cfg, koanf.UnmarshalConf{Tag: "mapstructure"}); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
}

cfg.Path = cfgFile
cfg.Dir = filepath.Dir(cfgFile)

return cfg, k, nil
}
78 changes: 78 additions & 0 deletions cmd/nrdot-collector-builder/cmd/manifest/update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2025 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package manifest

import (
"errors"
"os"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

type MockConfig struct {
mock.Mock
}

func (m *MockConfig) Validate() error {
args := m.Called()
return args.Error(0)
}

func (m *MockConfig) SetGoPath() error {
args := m.Called()
return args.Error(0)
}

func (m *MockConfig) ParseModules() error {
args := m.Called()
return args.Error(0)
}

func TestUpdateCmd_RunE(t *testing.T) {

// Load the test-config.yaml file
testConfigPath := "testdata/test-config.yaml"
yamlData, err := os.ReadFile(testConfigPath)
assert.NoError(t, err)

// Create a temporary file to simulate writing to a file
tempFile, err := os.CreateTemp("", "test-config-*.yaml")
assert.NoError(t, err)
defer os.Remove(tempFile.Name()) // Clean up the file after the test

// Write the loaded YAML data to the temporary file
_, err = tempFile.Write(yamlData)
assert.NoError(t, err)
tempFile.Close() // Close the file to ensure the changes are flushed

cmd := &cobra.Command{}
cmd.Flags().String("config", tempFile.Name(), "")

mockConfig := new(MockConfig)
mockConfig.On("Validate").Return(nil)
mockConfig.On("SetGoPath").Return(nil)
mockConfig.On("ParseModules").Return(nil)

err := UpdateCmd.RunE(cmd, []string{})
assert.NoError(t, err)

mockConfig.AssertExpectations(t)
}

func TestUpdateCmd_RunE_InvalidConfig(t *testing.T) {

cmd := &cobra.Command{}
cmd.Flags().String("config", "test-config.yaml", "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we don't use the invalid test data config? As far as I can tell it's unused?


mockConfig := new(MockConfig)
mockConfig.On("Validate").Return(errors.New("invalid configuration"))

err := UpdateCmd.RunE(cmd, []string{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid configuration")

mockConfig.AssertExpectations(t)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we support globbing, a test to validate that would be great.

30 changes: 30 additions & 0 deletions cmd/nrdot-collector-builder/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd

import (
"os"

"github.com/spf13/cobra"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "nrdot-collector-builder",
Short: "NRDOT client for building the OpenTelemetry Collector",
Long: `
A CLI tool for building the OpenTelemetry Collector with NRDOT extensions.
This tool allows you to create a custom OpenTelemetry Collector binary with NRDOT extensions and configurations.
It simplifies the process of building and deploying the collector with NRDOT-specific features.
`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {

err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
35 changes: 35 additions & 0 deletions cmd/nrdot-collector-builder/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module newrelic-collector-builder

go 1.24.1

require (
github.com/knadh/koanf/parsers/yaml v1.0.0
github.com/stretchr/testify v1.10.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/knadh/koanf/providers/file v1.2.0
github.com/knadh/koanf/v2 v2.2.0
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6 // indirect
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/mod v0.24.0
golang.org/x/sys v0.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
59 changes: 59 additions & 0 deletions cmd/nrdot-collector-builder/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v1.0.0 h1:PXyeHCRhAMKyfLJaoTWsqUTxIFeDMmdAKz3XVEslZV4=
github.com/knadh/koanf/parsers/yaml v1.0.0/go.mod h1:Q63VAOh/s6XaQs6a0TB2w9GFUuuPGvfYrCSWb9eWAQU=
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
github.com/knadh/koanf/v2 v2.2.0 h1:FZFwd9bUjpb8DyCWARUBy5ovuhDs1lI87dOEn2K8UVU=
github.com/knadh/koanf/v2 v2.2.0/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading
Loading