Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Metadata labels #21

Merged
merged 2 commits into from
May 20, 2024
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
55 changes: 34 additions & 21 deletions action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package action

import (
"context"
"errors"
"fmt"
"io"
"net/url"
"os"
"time"
Expand Down Expand Up @@ -273,28 +275,9 @@ func (a *Action) Apply() error {
// apply takes a file path and applies it to the BindPlane API. If an
// error is found in the response status, it will be returned
func (a *Action) apply(path string) error {
f, err := os.Open(path) // #nosec G304 user defined filepath
resources, err := decodeAnyResourceFile(path)
if err != nil {
return fmt.Errorf("unable to read file at path %s: %w", path, err)
}

resources := []*model.AnyResource{}
decoder := yaml.NewDecoder(f)
for {
var resource model.AnyResource
if err := decoder.Decode(&resource); err != nil {
if err.Error() == "EOF" {
break
}
// TODO(jsirianni): Should we continue and report the error after?
return fmt.Errorf("resource file %s is malformed, failed to unmarshal yaml: %w", path, err)
}
resources = append(resources, &resource)
}

if len(resources) == 0 {
a.Logger.Warn("No resources found in file", zap.String("file", path))
return nil
return fmt.Errorf("decode resources: %w", err)
}

resp, err := a.client.Apply(context.Background(), resources)
Expand Down Expand Up @@ -495,3 +478,33 @@ func (a *Action) WriteBack() error {

return nil
}

// decodeAnyResourceFile takes a file path and decodes it into a slice of
// model.AnyResource. If the file is empty, it will return an error.
func decodeAnyResourceFile(path string) ([]*model.AnyResource, error) {
f, err := os.Open(path) // #nosec G304 user defined filepath
if err != nil {
return nil, fmt.Errorf("unable to read file at path %s: %w", path, err)
}
defer f.Close()

resources := []*model.AnyResource{}
decoder := yaml.NewDecoder(f)
for {
resource := &model.AnyResource{}
if err := decoder.Decode(resource); err != nil {
if errors.Is(err, io.EOF) {
break
}
// TODO(jsirianni): Should we continue and report the error after?
return nil, fmt.Errorf("resource file %s is malformed, failed to unmarshal yaml: %w", path, err)
}
resources = append(resources, resource)
}

if len(resources) == 0 {
return nil, fmt.Errorf("no resources found in file: %s", path)
}

return resources, nil
}
24 changes: 24 additions & 0 deletions action/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,27 @@ func TestNew(t *testing.T) {
})
}
}

func TestDecodeAnyResourceFile(t *testing.T) {
resources, err := decodeAnyResourceFile("testdata/configuration.yaml")
require.NoError(t, err)
require.NotNil(t, resources)
require.Len(t, resources, 3)

for _, r := range resources {
require.Equal(t, "bindplane.observiq.com/v1", r.APIVersion)
require.Equal(t, "Configuration", r.Kind)
require.NotEmpty(t, r.Metadata.ID)
require.NotEmpty(t, r.Metadata.Name)
require.Len(t, r.Metadata.Labels, 1, "Expected 1 label")
platforms := []string{
"kubernetes-gateway",
"kubernetes-daemonset",
"kubernetes-deployment",
}
key := "platform"
v, ok := r.Metadata.Labels[key]
require.True(t, ok, "Expected label %s", key)
require.Contains(t, platforms, v, "Expected platform label to be one of %v, got %s", platforms, v)
}
}
153 changes: 153 additions & 0 deletions action/testdata/configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
apiVersion: bindplane.observiq.com/v1
kind: Configuration
metadata:
id: k8s-cluster
name: k8s-cluster
labels:
platform: kubernetes-deployment
spec:
contentType: ""
measurementInterval: ""
sources:
- id: 01HMS8GVNVFVD5TSWTKSJR1RY5
type: k8s_cluster
parameters:
- name: cluster_name
value: minikube
- name: node_conditions_to_report
value:
- Ready
- DiskPressure
- MemoryPressure
- PIDPressure
- NetworkUnavailable
- name: allocatable_types_to_report
value:
- cpu
- memory
- ephemeral-storage
- storage
- name: collection_interval
value: 60
- name: distribution
value: kubernetes
- id: 01HMS8GVNVFVD5TSWTKVNZS2JC
displayName: Production events
type: k8s_events
parameters:
- name: cluster_name
value: minikube
- name: namespaces
value:
- kube-system
- production
destinations:
- id: 01HMS8GVNVFVD5TSWTKZZNHB8R
name: bindplane-gateway-agent
selector:
matchLabels:
configuration: k8s-cluster
---
apiVersion: bindplane.observiq.com/v1
kind: Configuration
metadata:
id: k8s-gateway
name: k8s-gateway
labels:
platform: kubernetes-gateway
spec:
contentType: ""
measurementInterval: ""
sources:
- id: 01HMS8J2WPEVAX0RSCPW4ZNWT5
type: otlp
parameters:
- name: telemetry_types
value:
- Metrics
- Logs
- Traces
- name: listen_address
value: 0.0.0.0
- name: grpc_port
value: 4317
- name: http_port
value: 4318
- name: enable_tls
value: false
- name: cert_file
value: ""
- name: key_file
value: ""
- name: mutual_tls
value: false
- name: ca_file
value: ""
- name: enable_grpc_timeout
value: true
- name: grpc_max_connection_idle
value: 60
- name: grpc_max_connection_age
value: 60
- name: grpc_max_connection_age_grace
value: 300
destinations:
- id: 01HMS8J2WPEVAX0RSCQ1NZ5KB6
name: coralogix
- id: 01HMS8K1AEYPBCBN47ZFYM3X7J
name: elasticsearch-internal
- id: 01HMS8KV7K4N2WKJ3337T9KJ2M
name: prometheus-internal
selector:
matchLabels:
configuration: k8s-gateway
---
apiVersion: bindplane.observiq.com/v1
kind: Configuration
metadata:
id: k8s-node
name: k8s-node
labels:
platform: kubernetes-daemonset
spec:
contentType: ""
measurementInterval: ""
sources:
- id: 01HMS8ERRSFYE4KGQVQ2ZXXDZK
type: k8s_container
parameters:
- name: cluster_name
value: minikube
- name: log_source
value: file
- name: file_path
value:
- /var/log/containers/*.log
- name: exclude_file_path
value:
- /var/log/containers/observiq-*-collector-*
- /var/log/containers/bindplane-*-agent-*
- name: journald_path
value: ""
- name: start_at
value: end
- id: 01HMS8ERRSFYE4KGQVQ8DQC5X0
type: k8s_kubelet
parameters:
- name: cluster_name
value: minikube
- name: metric_groups
value:
- node
- pod
- container
- volume
- name: collection_interval
value: 60
destinations:
- id: 01HMS8HBK216AD6V30GKX968XK
name: bindplane-gateway-agent
selector:
matchLabels:
configuration: k8s-node
26 changes: 13 additions & 13 deletions internal/client/model/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package model
import "time"

type Metadata struct {
ID string `yaml:"id,omitempty" json:"id" mapstructure:"id"`
Name string `yaml:"name,omitempty" json:"name" mapstructure:"name"`
DisplayName string `yaml:"displayName,omitempty" json:"displayName,omitempty" mapstructure:"displayName"`
Description string `yaml:"description,omitempty" json:"description,omitempty" mapstructure:"description"`
Icon string `yaml:"icon,omitempty" json:"icon,omitempty" mapstructure:"icon"`
Labels Labels `yaml:"labels,omitempty" json:"labels" mapstructure:"labels"`
Hash string `yaml:"hash,omitempty" json:"hash,omitempty" mapstructure:"hash"`
Version int `yaml:"version,omitempty" json:"version,omitempty" mapstructure:"version"`
DateModified *time.Time `yaml:"dateModified,omitempty" json:"dateModified,omitempty" mapstructure:"dateModified"`
Deprecated bool `yaml:"deprecated,omitempty" json:"deprecated,omitempty" mapstructure:"deprecated"`
AdditionalInfo *AdditionalInfo `yaml:"additionalInfo,omitempty" json:"additionalInfo,omitempty" mapstructure:"additionalInfo"`
ResourceDocLink string `yaml:"resourceDocLink,omitempty" json:"resourceDocLink,omitempty" mapstructure:"resourceDocLink"`
Stability string `yaml:"stability,omitempty" json:"stability,omitempty" mapstructure:"stability"`
ID string `yaml:"id,omitempty" json:"id" mapstructure:"id"`
Name string `yaml:"name,omitempty" json:"name" mapstructure:"name"`
DisplayName string `yaml:"displayName,omitempty" json:"displayName,omitempty" mapstructure:"displayName"`
Description string `yaml:"description,omitempty" json:"description,omitempty" mapstructure:"description"`
Icon string `yaml:"icon,omitempty" json:"icon,omitempty" mapstructure:"icon"`
Labels map[string]string `yaml:"labels,omitempty" json:"labels" mapstructure:"labels"`
Hash string `yaml:"hash,omitempty" json:"hash,omitempty" mapstructure:"hash"`
Version int `yaml:"version,omitempty" json:"version,omitempty" mapstructure:"version"`
DateModified *time.Time `yaml:"dateModified,omitempty" json:"dateModified,omitempty" mapstructure:"dateModified"`
Deprecated bool `yaml:"deprecated,omitempty" json:"deprecated,omitempty" mapstructure:"deprecated"`
AdditionalInfo *AdditionalInfo `yaml:"additionalInfo,omitempty" json:"additionalInfo,omitempty" mapstructure:"additionalInfo"`
ResourceDocLink string `yaml:"resourceDocLink,omitempty" json:"resourceDocLink,omitempty" mapstructure:"resourceDocLink"`
Stability string `yaml:"stability,omitempty" json:"stability,omitempty" mapstructure:"stability"`
}
Loading