Skip to content

Commit

Permalink
feat: add cli command to update onprem token (#2066)
Browse files Browse the repository at this point in the history
Currently, users cannot update their on-prem token and are limited to
reinstalling Odigos in their cluster. This PR introduces the ability to
update the odigos-pro secret's data using the odigos cli

---------

Co-authored-by: Amir Blum <[email protected]>
Co-authored-by: Tamir David <[email protected]>
  • Loading branch information
3 people authored Jan 8, 2025
1 parent 82a098e commit a1c17d2
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 18 deletions.
29 changes: 28 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"version": "0.2.0",
"configurations": [

{
"name": "Remote Odiglet",
"type": "go",
Expand Down Expand Up @@ -64,7 +65,33 @@
"cwd": "${workspaceFolder}/cli",
"args": ["uninstall", "--yes"],
"buildFlags": "-tags=embed_manifests"
},
{
"name": "cli install",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cli",
"cwd": "${workspaceFolder}/cli",
"args": ["install", "--version", "ODIGOS_VERSION"],
"buildFlags": "-tags=embed_manifests"
},
{
"name": "cli pro",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cli",
"cwd": "${workspaceFolder}/cli",
"args": ["pro", "--onprem-token", "${input:onprem_token}"],
"buildFlags": "-tags=embed_manifests"
}
]
],
"inputs": [
{
"id": "onprem_token",
"type": "promptString",
"description": "Enter your onprem token"
}]
}

82 changes: 72 additions & 10 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,97 @@ This guide provides advanced instructions for contributors and maintainers, cove
### Step 1: Port Forward the Gateway or Data Collection Pod
Forward the relevant pod to your local machine to enable profiling access:

```bash
kubectl port-forward pod/<pod-name> -n odigos-system 1777:1777

```

### Step 2: Collect Profiling Data

- **CPU Profile**
Captures data about the amount of time your application spends executing functions. Use this profile to identify performance bottlenecks, optimize CPU-intensive operations, and analyze which parts of the code consume the most CPU resources.
Captures data about the time your application spends executing functions. Use this profile to identify performance bottlenecks, optimize CPU-intensive operations, and analyze which parts of the code consume the most CPU resources.

``` bash
curl -o cpu_profile.prof http://localhost:1777/debug/pprof/profile?seconds=30
```bash
curl -o cpu_profile.prof http://localhost:1777/debug/pprof/profile?seconds=30
```

- **Heap Memory Profile**
Captures a snapshot of memory currently in use by your application after the latest garbage collection. Use this profile to identify memory leaks, track high memory usage, and analyze memory consumption by specific parts of the code.
``` bash
Captures a snapshot of memory currently in use by your application after the latest garbage collection. Use this profile to identify memory leaks, track high memory usage, and analyze memory consumption by specific parts of the code.

```bash
curl -o heap.out http://localhost:1777/debug/pprof/heap
```

- **Historical Memory Allocation**
Provides insights into all memory allocations made by the program since it started running, including memory that has already been freed by the garbage collector (GC). This is useful for understanding memory allocation patterns and optimizing allocation behavior.
``` bash
Provides insights into all memory allocations made by the program since it started running, including memory that has already been freed by the garbage collector (GC). This is useful for understanding memory allocation patterns and optimizing allocation behavior.

```bash
curl -o allocs.out http://localhost:1777/debug/pprof/allocs
```

### Step 3: Analyze the Profiles
After collecting the profiling data, use the `go tool pprof` command to analyze the profiles visually in your web browser. Replace `<output file>` with the appropriate file (`cpu_profile.prof`, `heap.out`, or `allocs.out`):
``` bash

```bash
go tool pprof -http=:8080 <output file>
```

This opens an interactive interface in your browser where you can:
- **Visualize Hotspots**: View flame graphs or directed graphs for easy identification of bottlenecks.
- **Drill Down**: Explore specific functions or memory allocations for detailed insights.
- **Drill Down**: Explore specific functions or memory allocations for detailed insights.

---

## Debugging CLI Commands

### Debugging the `cli pro` Command

To debug the `cli pro` command in Visual Studio Code, use the following configuration in your `.vscode/launch.json` file:

```jsonc
{
"name": "cli pro",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cli",
"cwd": "${workspaceFolder}/cli",
"args": ["pro", "--onprem-token", "${input:onprem_token}"],
"buildFlags": "-tags=embed_manifests"
}
```

#### How to Use
1. Open the **Run and Debug** view in Visual Studio Code:
- Press `Ctrl+Shift+D` (Windows/Linux) or `Cmd+Shift+D` (macOS).
2. Select the `cli pro` configuration from the dropdown menu.
3. Click the green **Play** button to start debugging.
4. When prompted, enter your `onprem-token` value.
5. The debugger will start the `cli pro` command with the provided token and attach to the process for debugging.

---

### Debugging the `cli install` Command

To debug the `cli install` command in Visual Studio Code, use the following configuration in your `launch.json` file:

```jsonc
{
"name": "cli install",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cli",
"cwd": "${workspaceFolder}/cli",
"args": ["install", "--version", "ODIGOS_VERSION"],
"buildFlags": "-tags=embed_manifests"
}
```

#### How to Use
1. Open the **Run and Debug** view in Visual Studio Code:
- Press `Ctrl+Shift+D` (Windows/Linux) or `Cmd+Shift+D` (macOS).
2. Select the `cli install` configuration from the dropdown menu.
3. Replace `"ODIGOS_VERSION"` in the `args` section with the desired version number.
4. Click the green **Play** button to start debugging.
5. The debugger will start the `cli install` command with the specified version.

88 changes: 88 additions & 0 deletions cli/cmd/pro.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cmd

import (
"context"
"fmt"
"os"
"time"

"github.com/odigos-io/odigos/cli/cmd/resources"
cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context"
"github.com/odigos-io/odigos/cli/pkg/kube"
"github.com/odigos-io/odigos/k8sutils/pkg/consts"
odigosconsts "github.com/odigos-io/odigos/common/consts"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var proCmd = &cobra.Command{
Use: "pro",
Short: "manage odigos pro",
Long: `The pro command provides various operations and functionalities specifically designed for enterprise users. Use this command to access advanced features and manage your pro account.`,
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
client := cmdcontext.KubeClientFromContextOrExit(ctx)
ns, err := resources.GetOdigosNamespace(client, ctx)
if resources.IsErrNoOdigosNamespaceFound(err) {
fmt.Println("\033[31mERROR\033[0m no odigos installation found in the current cluster")
os.Exit(1)
} else if err != nil {
fmt.Printf("\033[31mERROR\033[0m Failed to check if Odigos is already installed: %s\n", err)
os.Exit(1)
}
onPremToken := cmd.Flag("onprem-token").Value.String()
err = updateOdigosToken(ctx, client, ns, onPremToken)
if err != nil {
fmt.Println("\033[31mERROR\033[0m Failed to update token:")
fmt.Println(err)
os.Exit(1)
}

fmt.Println()
fmt.Println("\u001B[32mSUCCESS:\u001B[0m Token updated successfully")
},
}

func updateOdigosToken(ctx context.Context, client *kube.Client, namespace string, onPremToken string) error {
secret, err := client.CoreV1().Secrets(namespace).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return fmt.Errorf("Tokens are not available in the open-source version of Odigos. Please contact Odigos team to inquire about pro version.")
}
return err
}
secret.Data[consts.OdigosOnpremTokenSecretKey] = []byte(onPremToken)

_, err = client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{})
if err != nil {
return err
}

daemonSet, err := client.AppsV1().DaemonSets(namespace).Get(ctx, "odiglet", metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get DaemonSet odiglet in namespace %s: %v", namespace, err)
}

// Modify the DaemonSet spec.template to trigger a rollout
if daemonSet.Spec.Template.Annotations == nil {
daemonSet.Spec.Template.Annotations = make(map[string]string)
}
daemonSet.Spec.Template.Annotations[odigosconsts.RolloutTriggerAnnotation] = time.Now().Format(time.RFC3339)

_, err = client.AppsV1().DaemonSets(namespace).Update(ctx, daemonSet, metav1.UpdateOptions{})
if err != nil {
fmt.Printf("Failed to restart Odiglets. Reason: %s\n", err)
fmt.Printf("To trigger a restart manually, run the following command:\n")
fmt.Printf("kubectl rollout restart daemonset odiglet -n %s\n", daemonSet.Namespace)
}

return nil
}

func init() {
rootCmd.AddCommand(proCmd)

proCmd.Flags().String("onprem-token", "", "On-prem token for Odigos")
proCmd.MarkFlagRequired("onprem-token")
}
1 change: 0 additions & 1 deletion cli/cmd/resources/odigospro/consts.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package odigospro

const (
odigosProSecretName = "odigos-pro"
odigosCloudTokenEnvName = "ODIGOS_CLOUD_TOKEN"
odigosCloudApiKeySecretKey = "odigos-cloud-api-key"
odigosOnpremTokenEnvName = "ODIGOS_ONPREM_TOKEN"
Expand Down
3 changes: 2 additions & 1 deletion cli/cmd/resources/odigospro/manifests.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package odigospro

import (
"github.com/odigos-io/odigos/k8sutils/pkg/consts"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand All @@ -21,7 +22,7 @@ func newOdigosProSecret(ns string, cloudApiKey string, onpremToken string) *core
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: odigosProSecretName,
Name: consts.OdigosProSecretName,
Namespace: ns,
},
StringData: data,
Expand Down
7 changes: 4 additions & 3 deletions cli/cmd/resources/odigospro/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/odigos-io/odigos/cli/pkg/kube"
"github.com/odigos-io/odigos/common"
"github.com/odigos-io/odigos/k8sutils/pkg/consts"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -35,7 +36,7 @@ func CloudTokenAsEnvVar() corev1.EnvVar {
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: odigosProSecretName,
Name: consts.OdigosProSecretName,
},
Key: odigosCloudApiKeySecretKey,
},
Expand All @@ -50,7 +51,7 @@ func OnPremTokenAsEnvVar() corev1.EnvVar {
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: odigosProSecretName,
Name: consts.OdigosProSecretName,
},
Key: odigosOnpremTokenSecretKey,
},
Expand All @@ -59,7 +60,7 @@ func OnPremTokenAsEnvVar() corev1.EnvVar {
}

func getCurrentOdigosProSecret(ctx context.Context, client *kube.Client, ns string) (*corev1.Secret, error) {
secret, err := client.CoreV1().Secrets(ns).Get(ctx, odigosProSecretName, metav1.GetOptions{})
secret, err := client.CoreV1().Secrets(ns).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return nil, nil
}
Expand Down
1 change: 1 addition & 0 deletions common/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
InstrumentationEnabled = "enabled"
InstrumentationDisabled = "disabled"
OdigosReportedNameAnnotation = "odigos.io/reported-name"
RolloutTriggerAnnotation = "rollout-trigger"

// GatewayMaxConnectionAge and GatewayMaxConnectionAgeGrace are the default values for the gateway collector.
GatewayMaxConnectionAge = "15s"
Expand Down
9 changes: 9 additions & 0 deletions k8sutils/pkg/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ const (
OdigosNodeCollectorConfigMapKey = "conf" // this key is different than the cluster collector value. not sure why
)

const (
OdigosProSecretName = "odigos-pro"
)

const (
OdigosEnvVarNamespace = "ODIGOS_WORKLOAD_NAMESPACE"
OdigosEnvVarContainerName = "ODIGOS_CONTAINER_NAME"
Expand All @@ -61,3 +65,8 @@ var (
// this value must be in sync with the one defined in the kubeVersion field in Chart.yaml
MinK8SVersionForInstallation = version.MustParse("v1.20.15-0")
)

const (
OdigosCloudApiKeySecretKey = "odigos-cloud-api-key"
OdigosOnpremTokenSecretKey = "odigos-onprem-token"
)
4 changes: 2 additions & 2 deletions k8sutils/pkg/utils/tiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/odigos-io/odigos/common"
"github.com/odigos-io/odigos/k8sutils/pkg/consts"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -13,7 +14,6 @@ import (
const (
odigosCloudApiKeySecretKey = "odigos-cloud-api-key"
odigosOnpremTokenSecretKey = "odigos-onprem-token"
odigosProSecretName = "odigos-pro"
)

func GetCurrentOdigosTier(ctx context.Context, namespaces string, client *kubernetes.Clientset) (common.OdigosTier, error) {
Expand All @@ -38,7 +38,7 @@ func GetCurrentOdigosTier(ctx context.Context, namespaces string, client *kubern

func getCurrentOdigosProSecret(ctx context.Context, namespace string, client *kubernetes.Clientset) (*corev1.Secret, error) {

secret, err := client.CoreV1().Secrets(namespace).Get(ctx, odigosProSecretName, metav1.GetOptions{})
secret, err := client.CoreV1().Secrets(namespace).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return nil, nil
}
Expand Down

0 comments on commit a1c17d2

Please sign in to comment.