Skip to content

Commit

Permalink
feat: Implement backend namespace and duration configuration (#30)
Browse files Browse the repository at this point in the history
* feat: Implement backend namespace and duration configuration

Signed-off-by: Leonardo Luz Almeida <[email protected]>

* fix cmd

Signed-off-by: Leonardo Luz Almeida <[email protected]>

* fix error message

Signed-off-by: Leonardo Luz Almeida <[email protected]>

* address review comments

Signed-off-by: Leonardo Luz Almeida <[email protected]>

* update manifests

Signed-off-by: Leonardo Luz Almeida <[email protected]>

---------

Signed-off-by: Leonardo Luz Almeida <[email protected]>
  • Loading branch information
leoluz authored Oct 9, 2024
1 parent dfe2299 commit c17f311
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 61 deletions.
13 changes: 11 additions & 2 deletions cmd/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,18 @@ type Options struct {
Backend BackendConfig
}

// BackendConfig defines the exposed backend configurations
type BackendConfig struct {
Port int `env:"EPHEMERAL_BACKEND_PORT, default=8888"`
// Port defines the port used to listen to http requests sent to this service
Port int `env:"EPHEMERAL_BACKEND_PORT, default=8888"`
// Kubeconfig is an optional configuration to allow connecting to a k8s cluster
// remotelly
Kubeconfig string `env:"KUBECONFIG"`
// Namespace must point to the namespace where this backend service is running
Namespace string `env:"EPHEMERAL_BACKEND_NAMESPACE, required"`
// DefaultAccessDuration defines the default duration to be used when creating
// AccessRequests
DefaultAccessDuration time.Duration `env:"EPHEMERAL_BACKEND_DEFAULT_ACCESS_DURATION, default=4h"`
}

// LogConfig defines the log configurations
Expand Down Expand Up @@ -116,7 +125,7 @@ func run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("error creating a new k8s persister: %w", err)
}

service := backend.NewDefaultService(persister, logger, "TODO: get namespace from config or env var")
service := backend.NewDefaultService(persister, logger, opts.Backend.Namespace, opts.Backend.DefaultAccessDuration)
handler := backend.NewAPIHandler(service, logger)

cli := humacli.New(func(hooks humacli.Hooks, options *BackendConfig) {
Expand Down
10 changes: 9 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/argoproj-labs/ephemeral-access/cmd/backend"
"github.com/argoproj-labs/ephemeral-access/cmd/controller"
"github.com/argoproj-labs/ephemeral-access/pkg/log"
"github.com/spf13/cobra"
)

Expand All @@ -18,13 +19,20 @@ func main() {
},
DisableAutoGenTag: true,
SilenceUsage: true,
SilenceErrors: true,
}

command.AddCommand(backend.NewCommand())
command.AddCommand(controller.NewCommand())

if err := command.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
msg := "ephemeral-access execution error"
logger, logerr := log.NewLogger()
if logerr != nil {
fmt.Fprintf(os.Stderr, "%s: %s", msg, err)
os.Exit(1)
}
logger.Error(err, msg)
os.Exit(1)
}
}
23 changes: 23 additions & 0 deletions config/backend/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-cm
labels:
app.kubernetes.io/name: argocd-ephemeral-access
app.kubernetes.io/managed-by: kustomize
# data:
## Log configurations
# backend.log.level: info
# backend.log.format: json

## The API server port
# backend.port: '8888'

## The address the metric endpoint binds to
# backend.metrics.address: :8883

## If set the metrics endpoint is served securely
# backend.metrics.secure: 'true'

## Defines the default duration to be used when creating AccessRequests
# backend.defaultAccessDuration: 4h
20 changes: 15 additions & 5 deletions config/backend/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,43 @@ spec:
- name: EPHEMERAL_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: config-cm
name: backend-cm
key: backend.log.level
optional: true
- name: EPHEMERAL_LOG_FORMAT
valueFrom:
configMapKeyRef:
name: config-cm
name: backend-cm
key: backend.log.format
optional: true
- name: EPHEMERAL_METRICS_ADDR
valueFrom:
configMapKeyRef:
name: config-cm
name: backend-cm
key: backend.metrics.address
optional: true
- name: EPHEMERAL_METRICS_SECURE
valueFrom:
configMapKeyRef:
name: config-cm
name: backend-cm
key: backend.metrics.secure
optional: true
- name: EPHEMERAL_BACKEND_PORT
valueFrom:
configMapKeyRef:
name: config-cm
name: backend-cm
key: backend.port
optional: true
- name: EPHEMERAL_BACKEND_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EPHEMERAL_BACKEND_DEFAULT_ACCESS_DURATION
valueFrom:
configMapKeyRef:
name: backend-cm
key: backend.defaultAccessDuration
optional: true
image: argoproj-labs/argocd-ephemeral-access:latest
imagePullPolicy: Always
name: backend
Expand Down
1 change: 1 addition & 0 deletions config/backend/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- config.yaml
- deployment.yaml
- metrics_service.yaml
- role_binding.yaml
Expand Down
5 changes: 0 additions & 5 deletions config/config/kustomization.yaml

This file was deleted.

24 changes: 9 additions & 15 deletions config/config/config.yaml → config/controller/config.yaml
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: config-cm
name: controller-cm
labels:
app.kubernetes.io/name: argocd-ephemeral-access
app.kubernetes.io/managed-by: kustomize
data:
{}
### Log configurations
# backend.log.level: info
# backend.log.format: json
# data:
## Log configurations
# controller.log.level: info
# controller.log.format: json

### Backend configurations
## The API server port.
# backend.port: '8888'
## The address the metric endpoint binds to.
# backend.metrics.address: :8883
## If set the metrics endpoint is served securely.
# backend.metrics.secure: 'true'

# ### Controller configurations
## The controler server port.
# controller.port: '8081'

## Enable leader election for controller manager.
# controller.leader.election.enabled: 'true'

## The address the probe endpoint binds to.
# controller.health.probe.address: :8082

## If set, HTTP/2 will be enabled for the metrics and webhook servers.
# controller.http2.enabled: 'true'

## Determines the interval the controller will requeue an AccessRequest.
# controller.requeue.interval: 1s

## The address the metric endpoint binds to.
# controller.metrics.address: :8083

## If set the metrics endpoint is served securely.
# controller.metrics.secure: 'true'
18 changes: 9 additions & 9 deletions config/controller/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,55 +30,55 @@ spec:
- name: EPHEMERAL_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.log.level
optional: true
- name: EPHEMERAL_LOG_FORMAT
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.log.format
optional: true
- name: EPHEMERAL_METRICS_ADDR
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.metrics.address
optional: true
- name: EPHEMERAL_METRICS_SECURE
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.metrics.secure
optional: true
- name: EPHEMERAL_CONTROLLER_PORT
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.port
optional: true
- name: EPHEMERAL_CONTROLLER_ENABLE_LEADER_ELECTION
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.leader.election.enabled
optional: true
- name: EPHEMERAL_CONTROLLER_HEALTH_PROBE_ADDR
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.health.probe.address
optional: true
- name: EPHEMERAL_CONTROLLER_ENABLE_HTTP2
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.http2.enabled
optional: true
- name: EPHEMERAL_CONTROLLER_REQUEUE_INTERVAL
valueFrom:
configMapKeyRef:
name: config-cm
name: controller-cm
key: controller.requeue.interval
optional: true
image: argoproj-labs/argocd-ephemeral-access:latest
Expand Down
1 change: 1 addition & 0 deletions config/controller/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- config.yaml
- deployment.yaml
- metrics_service.yaml
- namespace.yaml
1 change: 0 additions & 1 deletion config/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ namePrefix: argocd-ephemeral-access-
resources:
- ../crd
- ../rbac
- ../config
- ../controller
- ../backend
images:
Expand Down
27 changes: 16 additions & 11 deletions internal/backend/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"slices"
"strings"
"time"

api "github.com/argoproj-labs/ephemeral-access/api/ephemeral-access/v1alpha1"
"github.com/argoproj-labs/ephemeral-access/pkg/log"
Expand Down Expand Up @@ -35,9 +36,10 @@ type AccessRequestKey struct {

// DefaultService is the real Service implementation
type DefaultService struct {
k8s Persister
logger log.Logger
namespace string
k8s Persister
logger log.Logger
namespace string
accessRequestDuration time.Duration
}

var requestStateOrder = map[api.Status]int{
Expand All @@ -51,11 +53,12 @@ var requestStateOrder = map[api.Status]int{
}

// NewDefaultService will return a new DefaultService instance.
func NewDefaultService(c Persister, l log.Logger, namespace string) *DefaultService {
func NewDefaultService(c Persister, l log.Logger, namespace string, arDuration time.Duration) *DefaultService {
return &DefaultService{
k8s: c,
logger: l,
namespace: namespace,
k8s: c,
logger: l,
namespace: namespace,
accessRequestDuration: arDuration,
}
}

Expand Down Expand Up @@ -153,21 +156,23 @@ func (s *DefaultService) CreateAccessRequest(ctx context.Context, key *AccessReq
GenerateName: s.getAccessRequestPrefix(key.Username, roleName),
},
Spec: api.AccessRequestSpec{
Duration: v1.Duration{
Duration: s.accessRequestDuration,
},
Role: api.TargetRole{
TemplateName: binding.Spec.RoleTemplateRef.Name,
Ordinal: binding.Spec.Ordinal,
FriendlyName: binding.Spec.FriendlyName,
},
Subject: api.Subject{
Username: key.Username,
},
Application: api.TargetApplication{
Name: key.ApplicationName,
Namespace: key.ApplicationNamespace,
},
Subject: api.Subject{
Username: key.Username,
},
},
}
//TODO: Set duration. Configurable by the users? Server Config?
ar, err := s.k8s.CreateAccessRequest(ctx, ar)
if err != nil {
return nil, fmt.Errorf("error creating access request from k8s: %w", err)
Expand Down
13 changes: 8 additions & 5 deletions internal/backend/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import (
)

const (
ControllerNamespace = "test-controller-ns"
ControllerNamespace = "test-controller-ns"
AccessRequestDuration = time.Minute
)

type serviceFixture struct {
Expand All @@ -34,7 +35,7 @@ func serviceSetup(t *testing.T) *serviceFixture {
logger := mocks.NewMockLogger(t)
logger.EXPECT().Debug(mock.Anything, mock.Anything).Maybe()
logger.EXPECT().Info(mock.Anything, mock.Anything).Maybe()
svc := backend.NewDefaultService(persister, logger, ControllerNamespace)
svc := backend.NewDefaultService(persister, logger, ControllerNamespace, AccessRequestDuration)
return &serviceFixture{
persister: persister,
logger: logger,
Expand All @@ -53,9 +54,10 @@ func TestServiceCreateAccessRequest(t *testing.T) {
Username: "some-user",
}
ab := newDefaultAccessBinding()
f.persister.EXPECT().CreateAccessRequest(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, ar *api.AccessRequest) (*api.AccessRequest, error) {
return ar, nil
})
f.persister.EXPECT().CreateAccessRequest(mock.Anything, mock.Anything).
RunAndReturn(func(ctx context.Context, ar *api.AccessRequest) (*api.AccessRequest, error) {
return ar, nil
})

// When
result, err := f.svc.CreateAccessRequest(context.Background(), key, ab)
Expand All @@ -71,6 +73,7 @@ func TestServiceCreateAccessRequest(t *testing.T) {
assert.Equal(t, ab.Spec.FriendlyName, result.Spec.Role.FriendlyName)
assert.Equal(t, ab.Spec.Ordinal, result.Spec.Role.Ordinal)
assert.Equal(t, ab.Spec.RoleTemplateRef.Name, result.Spec.Role.TemplateName)
assert.Equal(t, AccessRequestDuration, result.Spec.Duration.Duration)
})
t.Run("will return error if k8s request fails", func(t *testing.T) {
// Given
Expand Down
Loading

0 comments on commit c17f311

Please sign in to comment.