Skip to content

Commit

Permalink
fix(cli): add optional password setting for headless redis client (#1…
Browse files Browse the repository at this point in the history
…9035) (#19039)

* chore: add optional password setting for headless redis client

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* fix: remove import cycle

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* fix: add shared SetOptionalRedisPasswordFromKubeConfig method

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* fix: export redis consts

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* test: add test cases for SetOptionalRedisPasswordFromKubeConfig()

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* chore: go mod tidy

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* fix: use require instead of assert

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>

* fix: Update common/common.go

Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Rachel Sheikh <sheikhrachel97@gmail.com>

---------

Signed-off-by: Rachel Sheikh <rsheikh@squareup.com>
Signed-off-by: Rachel Sheikh <sheikhrachel97@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
  • Loading branch information
sheikhrachel and crenshaw-dev committed Jul 15, 2024
1 parent 7fdddbe commit c141436
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 30 deletions.
9 changes: 2 additions & 7 deletions cmd/argocd/commands/admin/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,9 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
}

redisOptions := &redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}

secret, err := kubeClient.CoreV1().Secrets(namespace).Get(context.Background(), defaulRedisInitialPasswordSecretName, v1.GetOptions{})
if err == nil {
if _, ok := secret.Data[defaultResisInitialPasswordKey]; ok {
redisOptions.Password = string(secret.Data[defaultResisInitialPasswordKey])
}
if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, namespace, redisOptions); err != nil {
log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err)
}

client := redis.NewClient(redisOptions)
compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr)
if err != nil {
Expand Down
21 changes: 7 additions & 14 deletions cmd/argocd/commands/admin/redis_initial_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,18 @@ import (
"fmt"
"math/big"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"

"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"

"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
)

const (
defaulRedisInitialPasswordSecretName = "argocd-redis"
defaultResisInitialPasswordKey = "auth"
)

func generateRandomPassword() (string, error) {
Expand Down Expand Up @@ -52,8 +45,8 @@ func NewRedisInitialPasswordCommand() *cobra.Command {
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)

redisInitialPasswordSecretName := defaulRedisInitialPasswordSecretName
redisInitialPasswordKey := defaultResisInitialPasswordKey
redisInitialPasswordSecretName := common.DefaultRedisInitialPasswordSecretName
redisInitialPasswordKey := common.DefaultRedisInitialPasswordKey
fmt.Printf("Checking for initial Redis password in secret %s/%s at key %s. \n", namespace, redisInitialPasswordSecretName, redisInitialPasswordKey)

config, err := clientConfig.ClientConfig()
Expand Down
17 changes: 10 additions & 7 deletions cmd/argocd/commands/headless/headless.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,11 @@ import (
"sync"
"time"

"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
"github.com/argoproj/argo-cd/v2/common"

"github.com/alicebob/miniredis/v2"
"github.com/golang/protobuf/ptypes/empty"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/runtime"
Expand All @@ -26,7 +21,10 @@ import (
cache2 "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
Expand Down Expand Up @@ -254,13 +252,18 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
return fmt.Errorf("error running miniredis: %w", err)
}
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName}), time.Hour)

redisOptions := &redis.Options{Addr: mr.Addr()}
if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClientset, namespace, redisOptions); err != nil {
log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err)
}
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
EnableGZip: false,
Namespace: namespace,
ListenPort: *port,
AppClientset: appClientset,
DisableAuth: true,
RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}),
RedisClient: redis.NewClient(redisOptions),
Cache: servercache.NewCache(appstateCache, 0, 0, 0),
KubeClientset: kubeClientset,
DynamicClientset: dynamicClientset,
Expand Down
34 changes: 33 additions & 1 deletion common/common.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package common

import (
"errors"
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"time"

"github.com/pkg/errors"
"github.com/redis/go-redis/v9"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

// Component names
Expand Down Expand Up @@ -414,3 +419,30 @@ const TokenVerificationError = "failed to verify the token"
var TokenVerificationErr = errors.New(TokenVerificationError)

var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied")

// Redis password consts
const (
DefaultRedisInitialPasswordSecretName = "argocd-redis"
DefaultRedisInitialPasswordKey = "auth"
)

/*
SetOptionalRedisPasswordFromKubeConfig sets the optional Redis password if it exists in the k8s namespace's secrets.
We specify kubeClient as kubernetes.Interface to allow for mocking in tests, but this should be treated as a kubernetes.Clientset param.
*/
func SetOptionalRedisPasswordFromKubeConfig(ctx context.Context, kubeClient kubernetes.Interface, namespace string, redisOptions *redis.Options) error {
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(ctx, DefaultRedisInitialPasswordSecretName, v1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get secret %s/%s: %w", namespace, DefaultRedisInitialPasswordSecretName, err)
}
if secret == nil {
return fmt.Errorf("failed to get secret %s/%s: secret is nil", namespace, DefaultRedisInitialPasswordSecretName)
}
_, ok := secret.Data[DefaultRedisInitialPasswordKey]
if !ok {
return fmt.Errorf("secret %s/%s does not contain key %s", namespace, DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey)
}
redisOptions.Password = string(secret.Data[DefaultRedisInitialPasswordKey])
return nil
}
66 changes: 66 additions & 0 deletions common/common_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package common

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

"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubefake "k8s.io/client-go/kubernetes/fake"
)

// Test env var not set for EnvGRPCKeepAliveMin
Expand Down Expand Up @@ -44,3 +50,63 @@ func Test_GRPCKeepAliveMinIncorrectlySet(t *testing.T) {
grpcKeepAliveTime := GetGRPCKeepAliveTime()
assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime)
}

func TestSetOptionalRedisPasswordFromKubeConfig(t *testing.T) {
t.Parallel()
testCases := []struct {
name, namespace, expectedPassword, expectedErr string
secret *corev1.Secret
}{
{
name: "Secret exists with correct key",
namespace: "default",
expectedPassword: "password123",
expectedErr: "",
secret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName},
Data: map[string][]byte{DefaultRedisInitialPasswordKey: []byte("password123")},
},
},
{
name: "Secret does not exist",
namespace: "default",
expectedPassword: "",
expectedErr: fmt.Sprintf("failed to get secret default/%s", DefaultRedisInitialPasswordSecretName),
secret: nil,
},
{
name: "Secret exists without correct key",
namespace: "default",
expectedPassword: "",
expectedErr: fmt.Sprintf("secret default/%s does not contain key %s", DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey),
secret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName},
Data: map[string][]byte{},
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
var (
ctx = context.TODO()
kubeClient = kubefake.NewSimpleClientset()
redisOptions = &redis.Options{}
)
if tc.secret != nil {
if _, err := kubeClient.CoreV1().Secrets(tc.namespace).Create(ctx, tc.secret, metav1.CreateOptions{}); err != nil {
t.Fatalf("Failed to create secret: %v", err)
}
}
err := SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, tc.namespace, redisOptions)
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
}
require.Equal(t, tc.expectedPassword, redisOptions.Password)
})
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ require (
github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0 // indirect
Expand Down

0 comments on commit c141436

Please sign in to comment.