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

feat: packet traces feature for Retina CLI #198

Closed
wants to merge 6 commits into from
Closed
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
4 changes: 2 additions & 2 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package cmd

import (
"encoding/json"
"io/ioutil"
"os"

"github.com/microsoft/retina/pkg/client"
"github.com/microsoft/retina/pkg/log"
Expand Down Expand Up @@ -33,7 +33,7 @@ func NewRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
var config Config
file, _ := ioutil.ReadFile(ClientConfigPath)
file, _ := os.ReadFile(ClientConfigPath)
_ = json.Unmarshal([]byte(file), &config)
RetinaClient = client.NewRetinaClient(config.RetinaEndpoint)
},
Expand Down
14 changes: 10 additions & 4 deletions cli/cmd/trace.go → cli/cmd/trace/trace.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package cmd
package trace

import (
retinacmd "github.com/microsoft/retina/cli/cmd"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

func TraceCmd() *cobra.Command {
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "trace",
Short: "retrieve status or results from Retina",
Expand All @@ -19,9 +21,13 @@ func getTrace() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: "Retrieve network trace results with operation ID",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
operationID, _ := cmd.Flags().GetString("operationID")
return RetinaClient.GetTrace(operationID)
err := retinacmd.RetinaClient.GetTrace(operationID)
if err != nil {
return errors.Wrap(err, "failed to get trace")
}
return nil
},
}
cmd.Flags().String("operationID", "", "Network Trace Operation ID")
Expand Down
27 changes: 26 additions & 1 deletion deploy/registercrd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
RetinaCapturesYAMLpath = "retina.sh_captures.yaml"
RetinaEndpointsYAMLpath = "retina.sh_retinaendpoints.yaml"
MetricsConfigurationYAMLpath = "retina.sh_metricsconfigurations.yaml"
TracesConfigurationYAMLpath = "retina.sh_tracesconfigurations.yaml"
)

//go:embed manifests/controller/helm/retina/crds/retina.sh_captures.yaml
Expand All @@ -29,6 +30,9 @@ var RetinaEndpointsYAML []byte
//go:embed manifests/controller/helm/retina/crds/retina.sh_metricsconfigurations.yaml
var MetricsConfgurationYAML []byte

//go:embed manifests/controller/helm/retina/crds/retina.sh_tracesconfigurations.yaml
var TracesConfigurationYAML []byte

func GetRetinaCapturesCRD() (*apiextensionsv1.CustomResourceDefinition, error) {
retinaCapturesCRD := &apiextensionsv1.CustomResourceDefinition{}
if err := yaml.Unmarshal(RetinaCapturesYAML, &retinaCapturesCRD); err != nil {
Expand All @@ -55,7 +59,20 @@ func GetRetinaMetricsConfigurationCRD() (*apiextensionsv1.CustomResourceDefiniti
return retinaMetricsConfigurationCRD, nil
}

func InstallOrUpdateCRDs(ctx context.Context, enableRetinaEndpoint bool, apiExtensionsClient apiextv1.ApiextensionsV1Interface) (map[string]*apiextensionsv1.CustomResourceDefinition, error) {
func GetRetinaTracesConfigurationCRD() (*apiextensionsv1.CustomResourceDefinition, error) {
retinaTracesConfigurationCRD := &apiextensionsv1.CustomResourceDefinition{}
if err := yaml.Unmarshal(TracesConfigurationYAML, &retinaTracesConfigurationCRD); err != nil {
return nil, errors.Wrap(err, "error unmarshalling embedded tracesconfiguration")
}
return retinaTracesConfigurationCRD, nil
}

func InstallOrUpdateCRDs(
ctx context.Context,
enableRetinaEndpoint bool,
apiExtensionsClient apiextv1.ApiextensionsV1Interface,
enableTrace bool,
) (map[string]*apiextensionsv1.CustomResourceDefinition, error) {
crds := make(map[string]*apiextensionsv1.CustomResourceDefinition, 4)

retinaCapture, err := GetRetinaCapturesCRD()
Expand All @@ -78,6 +95,14 @@ func InstallOrUpdateCRDs(ctx context.Context, enableRetinaEndpoint bool, apiExte
}
crds[retinaMetricsConfiguration.GetObjectMeta().GetName()] = retinaMetricsConfiguration

if enableTrace {
retinaTracesConfiguration, err := GetRetinaTracesConfigurationCRD()
if err != nil {
return nil, err
}
crds[retinaTracesConfiguration.GetObjectMeta().GetName()] = retinaTracesConfiguration
}

for name, crd := range crds {
current, err := apiExtensionsClient.CustomResourceDefinitions().Create(ctx, crd, v1.CreateOptions{})
if apierrors.IsAlreadyExists(err) {
Expand Down
11 changes: 10 additions & 1 deletion deploy/registercrd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func TestGetCRD(t *testing.T) {
require.FileExists(t, fmt.Sprintf(full, RetinaCapturesYAMLpath))
require.FileExists(t, fmt.Sprintf(full, RetinaEndpointsYAMLpath))
require.FileExists(t, fmt.Sprintf(full, MetricsConfigurationYAMLpath))
require.FileExists(t, fmt.Sprintf(full, TracesConfigurationYAMLpath))

capture, err := GetRetinaCapturesCRD()
require.NoError(t, err)
Expand All @@ -38,12 +39,18 @@ func TestGetCRD(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, metrics)
require.NotEmpty(t, metrics.TypeMeta.Kind)

traces, err := GetRetinaTracesConfigurationCRD()
require.NoError(t, err)
require.NotNil(t, traces)
require.NotEmpty(t, traces.TypeMeta.Kind)
}

func TestInstallOrUpdateCRDs(t *testing.T) {
capture, _ := GetRetinaCapturesCRD()
endpoint, _ := GetRetinaEndpointCRD()
metrics, _ := GetRetinaMetricsConfigurationCRD()
traces, _ := GetRetinaTracesConfigurationCRD()

tests := []struct {
name string
Expand All @@ -58,6 +65,7 @@ func TestInstallOrUpdateCRDs(t *testing.T) {
"captures.retina.sh": capture,
"retinaendpoints.retina.sh": endpoint,
"metricsconfigurations.retina.sh": metrics,
"tracesconfigurations.retina.sh": traces,
},
},
{
Expand All @@ -66,6 +74,7 @@ func TestInstallOrUpdateCRDs(t *testing.T) {
want: map[string]*apiextensionsv1.CustomResourceDefinition{
"captures.retina.sh": capture,
"metricsconfigurations.retina.sh": metrics,
"tracesconfigurations.retina.sh": traces,
},
},
}
Expand All @@ -75,7 +84,7 @@ func TestInstallOrUpdateCRDs(t *testing.T) {
apiExtensionsClient := &apiextv1fake.FakeApiextensionsV1{
Fake: &kubeClient.Fake,
}
got, err := InstallOrUpdateCRDs(context.Background(), tt.enableRetinaEndpoint, apiExtensionsClient)
got, err := InstallOrUpdateCRDs(context.Background(), tt.enableRetinaEndpoint, apiExtensionsClient, true)
if (err != nil) != tt.wantErr {
require.NoError(t, err)
return
Expand Down
70 changes: 70 additions & 0 deletions docs/CRDs/TracesConfiguration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# TraceConfiguration

> **Note:** This feature is currently under experimental development.
Copy link
Contributor

Choose a reason for hiding this comment

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

add a comment here that this is not implemented in bold headings ?


## Overview

The `TraceConfiguration` CustomResourceDefinition (CRD) introduces a custom resource named `TraceConfiguration` that enables users to configure packet traces in a Kubernetes cluster. Packet traces can be tailored to specific use cases, offering the flexibility to capture detailed network data for debugging or continuous streaming of traces for security purposes.

## CRD Specification

The full specification for the `MetricsConfiguration` CRD can be found in the [TraceConfiguration CRD](https://github.com/microsoft/retina/blob/main/deploy/manifests/controller/helm/retina/crds/retina.sh_tracesconfigurations.yaml) file.

The `TraceConfiguration` CRD is defined with the following specifications:

- **API Group:** retina.sh
- **API Version:** v1alpha1
- **Kind:** TraceConfiguration
- **Plural:** traceconfigurations
- **Singular:** traceconfiguration
- **Scope:** Namespaced

### Fields

- **spec.traceConfigurations:** Specifies the detailed configuration options for packet tracing. It includes the following properties:
- `captureLevel`: Specifies the capture level, which can be set to `allPackets` or `firstPacket` (default).
- `includeLayer7Data`: Indicates whether layer 7 data (HTTP, DNS, TLS) should be included in the trace (default is `false`).
- `from`: Specifies the source entities from which packets will be captured, including IP blocks, namespaces, pods, and more.
- `to`: Specifies the destination entities to which packets will be captured, including IP blocks, services, and more.
- `ports`: Specifies the ports and protocols to capture packets for.

- **spec.tracePoints:** Specifies the types of trace points to capture, such as pod, nodeToPod, and nodeToNetwork.

- **spec.outputConfiguration:** Specifies the output destination and connection configuration for trace data. It includes the following properties:
- `destination`: Specifies the destination for trace data, which can be `stdout`, `azuretable`, `loganalytics`, or `opentelemetry`.
- `connectionConfiguration`: Specifies connection-related configuration options.

- **status:** Describes the status of the trace configuration, including the current state, reason, and accepted specification.

## Usage

### Configuring Packet Traces

To configure packet traces, create a YAML manifest file with the desired specifications and apply it to the cluster using `kubectl apply`:

```yaml
apiVersion: retina.sh/v1alpha1
kind: TraceConfiguration
metadata:
name: example-trace-configuration
spec:
traceConfigurations:
- captureLevel: firstPacket
includeLayer7Data: true
from:
- ipBlock:
cidr: 10.0.0.0/16
except:
- 10.0.0.5
to:
- namespaceSelector:
label: value
ports:
- port: "80"
protocol: TCP
tracePoints:
- pod
- nodeToPod
outputConfiguration:
destination: stdout
```
5 changes: 5 additions & 0 deletions operator/cache/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ type MetricsConfigurationCacheObject struct {
MetricsConfiguration *retinav1alpha1.MetricsConfiguration
}

type TraceConfigurationCacheObject struct {
Key string // cache.MetaNamespaceKeyFunc
TraceConfiguration *retinav1alpha1.TraceConfiguration
}

type RetinaEndpoint struct {
sync.RWMutex
Name string
Expand Down
4 changes: 3 additions & 1 deletion operator/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ type OperatorConfig struct {
LogLevel string `yaml:"logLevel"`
// EnableRetinaEndpoint indicates whether to enable RetinaEndpoint
EnableRetinaEndpoint bool `yaml:"enableRetinaEndpoint"`
RemoteContext bool `yaml:"remoteContext"`
// EnableTrace indicates whether to enable trace. This is a WIP feature.
EnableTrace bool `yaml:"enableTrace"`
RemoteContext bool `yaml:"remoteContext"`
}
18 changes: 1 addition & 17 deletions operator/main.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
Copyright 2023.

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.
*/

package main

import (
Expand Down Expand Up @@ -146,7 +130,7 @@ func main() {

if oconfig.InstallCRDs {
mainLogger.Sugar().Infof("Installing CRDs")
crds, err := deploy.InstallOrUpdateCRDs(ctx, oconfig.EnableRetinaEndpoint, clientset)
crds, err := deploy.InstallOrUpdateCRDs(ctx, oconfig.EnableRetinaEndpoint, clientset, oconfig.EnableTrace) //nolint // shadowing is fine here
if err != nil {
mainLogger.Error("unable to register CRDs", zap.Error(err))
os.Exit(1)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
/*
Copyright 2023.
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.
*/

package metricsconfigurationcontroller

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
/*
Copyright 2023.
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.
*/

package metricsconfigurationcontroller

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
/*
Copyright 2023.
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.
*/

package metricsconfigurationcontroller

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
/*
Copyright 2023.
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.
*/

package metricsconfigurationcontroller

import (
Expand Down
16 changes: 0 additions & 16 deletions pkg/controllers/operator/pod/pod_controller.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
Copyright 2023.

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.
*/

package podcontroller

import (
Expand Down
16 changes: 0 additions & 16 deletions pkg/controllers/operator/pod/pod_controller_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
Copyright 2023.

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.
*/

package podcontroller

import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func New(client client.Client, podchannel chan cache.PodCacheObject) *RetinaEndp

// NOTE(mainrerd): Chances are that pod cache channel lost pods events during controller manager restart, we need to
// have full-set reconciliation to make sure all RetinaEndpoints are reconciled to Pods.
// when a pod reaches here, it indicates that there is a metricsconfiguration that references it,
// when a pod reaches here, it indicates that there is a metricsconfiguration (or tracesconfiguration if enabled) that references it,
// or doesn't and a RetinaEndpoint needs to be created or updated.
// This is a blocking function, and will wait on the pod channel until a pod is received.
func (r *RetinaEndpointReconciler) ReconcilePod(pctx context.Context) {
Expand Down
Loading
Loading