Skip to content

Commit

Permalink
feat: introduce scope (#270)
Browse files Browse the repository at this point in the history
Signed-off-by: Smuu <[email protected]>
Co-authored-by: Jose Ramon Mañes <[email protected]>
  • Loading branch information
smuu and tty47 authored Apr 15, 2024
1 parent abd7ffc commit 738c810
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 131 deletions.
31 changes: 10 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,15 @@ This section will guide you on how to set up and run **knuu**.

### Prerequisites

1. **Docker**: Knuu requires Docker to run
> You can install Docker by following the instructions [here](https://docs.docker.com/get-docker/).
2. **Kubernetes cluster**: Set up access to a Kubernetes cluster using a kubeconfig.
> In case you have no Kubernets cluster running yet, you can get more information [here](https://kubernetes.io/docs/setup/).
1. **Kubernetes cluster**: Set up access to a Kubernetes cluster using a kubeconfig.
> In case you have no Kubernetes cluster running yet, you can get more information [here](https://kubernetes.io/docs/setup/).
3. **'test' Namespace**: Create a namespace called 'test' in your Kubernetes cluster.
> **Note:** The used namespace can be changed by setting the `KNUU_NAMESPACE` environment variable.
2. **Docker**: Knuu uses Docker by default. If `KNUU_BUILDER` is not explicitly set to `kubernetes`, Docker is required to run Knuu.
> You can install Docker by following the instructions [here](https://docs.docker.com/get-docker/).
### Writing Tests

The documentation you can find [here](https://pkg.go.dev/github.com/celestiaorg/knuu).
The documentation you can find [here](https://pkg.go.dev/github.com/celestiaorg/knuu).

Simple example:

Expand Down Expand Up @@ -175,19 +172,11 @@ go test -v ./...

You can set the following environment variables to change the behavior of knuu:

| Environment Variable | Description | Possible Values | Default |
|-----------------------------|-----------------------------------------------------------------------------------------------------------------|----------------------------------|----------|
| `KNUU_TIMEOUT` | The timeout for the tests. | Any valid duration | `60m` |
| `KNUU_NAMESPACE` | The namespace where the instances will be created. | Any valid namespace name | `test` |
| `KNUU_BUILDER` | The builder to use for building images. | `docker`, `kubernetes` | `docker` |
| `KNUU_DEDICATED_NAMESPACE` | Creates and deletes a dedicated namespace for the test after `KNUU_TIMEOUT`. See note below for more details.* | `true`, `false` | `false`
| `DEBUG_LEVEL` | The debug level. | `debug`, `info`, `warn`, `error` | `info` |

* Note on KNUU_DEDICATED_NAMESPACE:

When set to true, this environment variable tells knuu to create a unique namespace for running tests, which will be automatically deleted after the specified timeout period (`KNUU_TIMEOUT`).
The created namespace will have a prefix of `knuu-` followed by a unique identifier (unique id of the test), which ensures that each test run has its isolated environment within the Kubernetes cluster.
The unique identifier is an autogenerated timestamp that uniquely identifies each test run, however, developers have the option to specify this identifier.
| Environment Variable | Description | Possible Values | Default |
| --- | --- | --- | --- |
| `KNUU_TIMEOUT` | The timeout for the tests. | Any valid duration | `60m` |
| `KNUU_BUILDER` | The builder to use for building images. | `docker`, `kubernetes` | `docker` |
| `DEBUG_LEVEL` | The debug level. | `debug`, `info`, `warn`, `error` | `info` |

## Contributing

Expand Down
44 changes: 4 additions & 40 deletions pkg/k8s/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/sirupsen/logrus"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
Expand All @@ -20,16 +18,14 @@ var (
// clientset is a global variable that holds a kubernetes clientset.
clientset *kubernetes.Clientset

// namespacePath path in the filesystem to the namespace name
namespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
// tokenPath path in the filesystem to the service account token
tokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
// certPath path in the filesystem to the ca.crt
certPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)

// Initialize sets up the Kubernetes client with the appropriate configuration.
func Initialize(identifier string) error {
// Initialize sets up the Kubernetes client.
func Initialize() error {
k8sConfig, err := getClusterConfig()
if err != nil {
return fmt.Errorf("retrieving the Kubernetes config: %w", err)
Expand All @@ -40,38 +36,6 @@ func Initialize(identifier string) error {
return fmt.Errorf("creating clientset for Kubernetes: %w", err)
}

var namespaceName string
useDedicatedNamespace, _ := strconv.ParseBool(os.Getenv("KNUU_DEDICATED_NAMESPACE"))

// Check if the program is running in a Kubernetes cluster environment
if isClusterEnvironment() {
// Read the namespace from the pod's spec
namespaceBytes, err := os.ReadFile(namespacePath)
if err != nil {
return fmt.Errorf("reading namespace from pod's spec: %w", err)
}
namespaceName = string(namespaceBytes)
logrus.Debugf("Using namespace from pod spec: %s", namespaceName)
} else if useDedicatedNamespace {
namespaceName, err = InitializeNamespace(identifier)
if err != nil {
return fmt.Errorf("initializing dedicated namespace: %w", err)
}
logrus.Debugf("KNUU_DEDICATED_NAMESPACE enabled, namespace generated: %s", namespaceName)
} else {
// Use KNUU_NAMESPACE or fallback to a default if it's not set
namespaceName = os.Getenv("KNUU_NAMESPACE")
if namespaceName == "" {
namespaceName = "test"
}
logrus.Debugf("KNUU_DEDICATED_NAMESPACE not specified, namespace to use: %s", namespaceName)
}

// Set the namespace
setNamespace(namespaceName)

logrus.Infof("Namespace where the test runs: %s", namespaceName)

return nil
}

Expand Down Expand Up @@ -117,15 +81,15 @@ func isNotFound(err error) bool {
// precompile the regular expression to avoid recompiling it on every function call
var invalidCharsRegexp = regexp.MustCompile(`[^a-z0-9-]+`)

// sanitizeName ensures compliance with Kubernetes DNS-1123 subdomain names. It:
// SanitizeName ensures compliance with Kubernetes DNS-1123 subdomain names. It:
// 1. Converts the input string to lowercase.
// 2. Replaces underscores and any non-DNS-1123 compliant characters with hyphens.
// 3. Trims leading and trailing hyphens.
// 4. Ensures the name does not exceed 63 characters, trimming excess characters if necessary
// and ensuring it does not end with a hyphen after trimming.
//
// Use this function to sanitize strings to be used as Kubernetes names for resources.
func sanitizeName(name string) string {
func SanitizeName(name string) string {
sanitized := strings.ToLower(name)
// Replace underscores and any other disallowed characters with hyphens
sanitized = invalidCharsRegexp.ReplaceAllString(sanitized, "-")
Expand Down
70 changes: 47 additions & 23 deletions pkg/k8s/k8s_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,67 @@ import (
"fmt"

"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

// InitializeNamespace sets up the namespace based on the KNUU_DEDICATED_NAMESPACE environment variable
func InitializeNamespace(identifier string) (string, error) {
namespaceName := "knuu-" + sanitizeName(identifier)
logrus.Debugf("namespace random generated: %s", namespaceName)
if err := createNamespace(Clientset(), namespaceName); err != nil {
return "", fmt.Errorf("failed to create dedicated namespace: %v", err)
}

logrus.Debugf("full namespace name generated: %s", namespaceName)

return namespaceName, nil
}
// CreateNamespace creates a new namespace if it does not exist
func CreateNamespace(name string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

// createNamespace creates a new namespace if it does not exist
func createNamespace(clientset *kubernetes.Clientset, name string) error {
ctx := context.TODO()
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}

_, err := clientset.CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{})
_, err := Clientset().CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{})
if err != nil {
if errors.IsAlreadyExists(err) {
fmt.Printf("Namespace %s already exists, continuing.\n", name)
return nil
if !errors.IsAlreadyExists(err) {
return fmt.Errorf("error creating namespace %s: %v", name, err)
}
return fmt.Errorf("error creating namespace %s: %v", name, err)
logrus.Debugf("Namespace %s already exists, continuing.\n", name)
}
logrus.Debugf("Namespace %s created.\n", name)

return nil
}

// DeleteNamespace deletes an existing namespace
func DeleteNamespace(name string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

err := Clientset().CoreV1().Namespaces().Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
return fmt.Errorf("error deleting namespace %s: %v", name, err)
}

return nil
}

// GetNamespace retrieves an existing namespace
func GetNamespace(name string) (*corev1.Namespace, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

namespace, err := Clientset().CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("error getting namespace %s: %v", name, err)
}

return namespace, nil
}

// NamespaceExists checks if a namespace exists
func NamespaceExists(name string) bool {
_, err := GetNamespace(name)
if err != nil {
logrus.Debugf("Namespace %s does not exist, err: %v", name, err)
return false
}

return true
}
4 changes: 2 additions & 2 deletions pkg/k8s/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func Namespace() string {
return namespace
}

// setNamespace updates the namespace to the provided string.
func setNamespace(newNamespace string) {
// SetNamespace sets the used namespace to the provided string.
func SetNamespace(newNamespace string) {
namespace = newNamespace
}
2 changes: 1 addition & 1 deletion pkg/knuu/instance_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (i *Instance) getLabels() map[string]string {
return map[string]string{
"app": i.k8sName,
"k8s.kubernetes.io/managed-by": "knuu",
"knuu.sh/test-run-id": identifier,
"knuu.sh/scope": testScope,
"knuu.sh/test-started": startTime,
"knuu.sh/name": i.name,
"knuu.sh/k8s-name": i.k8sName,
Expand Down
61 changes: 52 additions & 9 deletions pkg/knuu/instance_otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package knuu
import (
"fmt"

"github.com/celestiaorg/knuu/pkg/k8s"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -122,6 +123,16 @@ type TLS struct {
type Service struct {
Extensions []string `yaml:"extensions,omitempty"`
Pipelines Pipelines `yaml:"pipelines,omitempty"`
Telemetry Telemetry `yaml:"telemetry,omitempty"` // Added Telemetry field
}

type Telemetry struct {
Metrics MetricsTelemetry `yaml:"metrics,omitempty"`
}

type MetricsTelemetry struct {
Address string `yaml:"address,omitempty"`
Level string `yaml:"level,omitempty"` // Options are basic, normal, detailed
}

type Pipelines struct {
Expand All @@ -130,8 +141,9 @@ type Pipelines struct {
}

type Metrics struct {
Receivers []string `yaml:"receivers,omitempty"`
Exporters []string `yaml:"exporters,omitempty"`
Receivers []string `yaml:"receivers,omitempty"`
Exporters []string `yaml:"exporters,omitempty"`
Processors []string `yaml:"processors,omitempty"`
}

type Traces struct {
Expand All @@ -143,6 +155,7 @@ type Traces struct {
type Processors struct {
Batch Batch `yaml:"batch,omitempty"`
MemoryLimiter MemoryLimiter `yaml:"memory_limiter,omitempty"`
Attributes Attributes `yaml:"attributes,omitempty"`
}

type Batch struct{}
Expand All @@ -153,6 +166,16 @@ type MemoryLimiter struct {
CheckInterval string `yaml:"check_interval,omitempty"`
}

type Attributes struct {
Actions []Action `yaml:"actions,omitempty"`
}

type Action struct {
Key string `yaml:"key,omitempty"`
Value string `yaml:"value,omitempty"`
Action string `yaml:"action,omitempty"`
}

func (i *Instance) createOtelCollectorInstance() (*Instance, error) {
otelAgent, err := NewInstance("otel-agent")
if err != nil {
Expand Down Expand Up @@ -240,6 +263,15 @@ func (i *Instance) createPrometheusReceiver() Prometheus {
},
},
},
{
JobName: "internal-telemetry",
ScrapeInterval: "10s",
StaticConfigs: []StaticConfig{
{
Targets: []string{"localhost:8888"},
},
},
},
},
},
}
Expand Down Expand Up @@ -349,6 +381,7 @@ func (i *Instance) prepareMetricsForServicePipeline() Metrics {
if i.obsyConfig.prometheusRemoteWriteExporterEndpoint != "" {
metrics.Exporters = append(metrics.Exporters, "prometheusremotewrite")
}
metrics.Processors = []string{"attributes"}
return metrics
}

Expand All @@ -366,6 +399,7 @@ func (i *Instance) prepareTracesForServicePipeline() Traces {
if i.obsyConfig.jaegerEndpoint != "" {
traces.Exporters = append(traces.Exporters, "jaeger")
}
traces.Processors = []string{"attributes"}
return traces
}

Expand All @@ -379,22 +413,31 @@ func (i *Instance) createService() Service {
pipelines.Metrics = i.prepareMetricsForServicePipeline()
pipelines.Traces = i.prepareTracesForServicePipeline()

telemetry := Telemetry{
Metrics: MetricsTelemetry{
Address: "localhost:8888",
Level: "basic",
},
}

return Service{
Extensions: extensions,
Pipelines: pipelines,
Telemetry: telemetry,
}
}

func (i *Instance) createProcessors() Processors {
processors := Processors{}

if i.obsyConfig.jaegerGrpcPort != 0 {
processors.Batch = Batch{}
processors.MemoryLimiter = MemoryLimiter{
LimitMiB: 400,
SpikeLimitMiB: 100,
CheckInterval: "5s",
}
processors.Attributes = Attributes{
Actions: []Action{
{
Key: "namespace",
Value: k8s.Namespace(),
Action: "insert",
},
},
}

return processors
Expand Down
Loading

0 comments on commit 738c810

Please sign in to comment.