Skip to content

Commit

Permalink
Add teleport-tbot feature flag (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuladhar authored Aug 19, 2024
1 parent 661c6d1 commit 37386fd
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added tbot feature flag, enabled with `--tbot` flag
- If tbot feature flag is set, creates configmap and append to tbot app extra config for generating kubeconfig.

## [0.9.3] - 2024-05-08

### Changed
Expand Down
3 changes: 3 additions & 0 deletions helm/teleport-operator/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ spec:
image: "{{ .Values.registry.domain }}/{{ .Values.image.name }}:{{ .Chart.Version }}"
args:
- "--namespace={{ include "resource.default.namespace" . }}"
{{- if .Values.tbot.enabled }}
- "--tbot"
{{- end }}
ports:
- name: metrics
protocol: TCP
Expand Down
8 changes: 8 additions & 0 deletions helm/teleport-operator/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@
}
}
}
},
"tbot": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
}
}
}
3 changes: 3 additions & 0 deletions helm/teleport-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ affinity:
matchExpressions:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"

tbot:
enabled: false
41 changes: 36 additions & 5 deletions internal/controller/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ const identityExpirationPeriod = 20 * time.Minute

// ClusterReconciler reconciles a Cluster object
type ClusterReconciler struct {
Client client.Client
Log logr.Logger
Scheme *runtime.Scheme
Teleport *teleport.Teleport
Namespace string
Client client.Client
Log logr.Logger
Scheme *runtime.Scheme
Teleport *teleport.Teleport
IsBotEnabled bool
Namespace string
}

//+kubebuilder:rbac:groups=cluster.x-k8s.io.giantswarm.io,resources=clusters,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -118,6 +119,20 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
return ctrl.Result{}, microerror.Mask(err)
}

if r.IsBotEnabled {
if err := r.Teleport.DeleteBotAppExtraConfig(ctx, log, r.Client, cluster.Name); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}

if err := r.Teleport.DeleteTbotConfigMap(ctx, log, r.Client, cluster.Name, key.TeleportBotNamespace); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}

if err := r.Teleport.DeleteKubeconfigSecret(ctx, log, r.Client, cluster.Name, key.TeleportBotNamespace); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}
}

// Remove finalizer from the Cluster CR
if controllerutil.ContainsFinalizer(cluster, key.TeleportOperatorFinalizer) {
if err := teleport.RemoveFinalizer(ctx, log, cluster, r.Client); err != nil {
Expand Down Expand Up @@ -208,6 +223,22 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
}

if r.IsBotEnabled {
secret, err := r.Teleport.GetKubeconfigSecret(ctx, r.Client, cluster.Name, key.TeleportBotNamespace)
if err != nil {
return ctrl.Result{}, microerror.Mask(err)
}
if secret == nil {
if err := r.Teleport.EnsureTbotConfigMap(ctx, log, r.Client, cluster.Name, key.TeleportBotNamespace, registerName); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}

if err := r.Teleport.EnsureBotAppExtraConfig(ctx, log, r.Client, cluster.Name); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}
}
}

// We need to requeue to check the teleport token validity
// and update secret for the cluster, if it expires
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
Expand Down
18 changes: 18 additions & 0 deletions internal/pkg/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const (
TeleportOperatorLabelValue = "teleport-operator"
TeleportOperatorConfigName = "teleport-operator"
TeleportBotSecretName = "identity-output"
TeleportBotNamespace = "giantswarm"
TeleportBotAppName = "teleport-tbot"
TeleportKubeTokenValidity = 720 * time.Hour
TeleportNodeTokenValidity = 720 * time.Hour

Expand All @@ -30,10 +32,18 @@ func GetConfigmapName(clusterName string, appName string) string {
return fmt.Sprintf("%s-%s-config", clusterName, appName)
}

func GetTbotConfigmapName(clusterName string) string {
return fmt.Sprintf("teleport-tbot-%s-config", clusterName)
}

func GetSecretName(clusterName string) string {
return fmt.Sprintf("%s-teleport-join-token", clusterName)
}

func GetKubeconfigSecretName(clusterName string) string {
return fmt.Sprintf("teleport-%s-kubeconfig", clusterName)
}

func GetRegisterName(managementClusterName, clusterName string) string {
return fmt.Sprintf("%s-%s", managementClusterName, clusterName)
}
Expand All @@ -59,3 +69,11 @@ kubeClusterName: "%s"

return fmt.Sprintf(dataTpl, authToken, proxyAddr, kubeClusterName)
}

func GetTbotConfigmapDataFromTemplate(kubeClusterName string, clusterName string) string {
dataTpl := `outputs:
%s: "%s"
`

return fmt.Sprintf(dataTpl, kubeClusterName, clusterName)
}
128 changes: 128 additions & 0 deletions internal/pkg/teleport/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package teleport

import (
"context"
"reflect"

"github.com/giantswarm/apiextensions-application/api/v1alpha1"
"github.com/giantswarm/microerror"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/giantswarm/teleport-operator/internal/pkg/key"
)

func (t *Teleport) getBotExtraConfig(clusterName string) v1alpha1.AppExtraConfig {
return v1alpha1.AppExtraConfig{
Kind: "configMap",
Name: key.GetTbotConfigmapName(clusterName),
Namespace: key.TeleportBotNamespace,
Priority: 25}
}

func (t *Teleport) appendBotExtraConfig(appExtraConfigs []v1alpha1.AppExtraConfig, extraConfig v1alpha1.AppExtraConfig) []v1alpha1.AppExtraConfig {
extraConfigs := []v1alpha1.AppExtraConfig{}
if appExtraConfigs == nil {
return append(extraConfigs, extraConfig)
}
shouldAppend := true
for _, config := range appExtraConfigs {
if reflect.DeepEqual(config, extraConfig) {
shouldAppend = false
break
}
}
if shouldAppend {
extraConfigs = append(appExtraConfigs, extraConfig)
} else {
extraConfigs = appExtraConfigs
}
return extraConfigs
}

func (t *Teleport) removeBotExtraConfig(appExtraConfigs []v1alpha1.AppExtraConfig, extraConfig v1alpha1.AppExtraConfig) []v1alpha1.AppExtraConfig {
extraConfigs := []v1alpha1.AppExtraConfig{}
for _, config := range appExtraConfigs {
if reflect.DeepEqual(config, extraConfig) {
continue
}
extraConfigs = append(extraConfigs, config)
}
return extraConfigs
}

func (t *Teleport) getBotApp(ctx context.Context, ctrlClient client.Client, clusterName string) (*v1alpha1.App, error) {
app := &v1alpha1.App{}
key := client.ObjectKey{Name: key.TeleportBotAppName, Namespace: key.TeleportBotNamespace}
if err := ctrlClient.Get(ctx, key, app); err != nil {
return app, err
}
return app, nil
}

func (t *Teleport) EnsureBotAppExtraConfig(ctx context.Context, log logr.Logger, ctrlClient client.Client, clusterName string) error {
app, err := t.getBotApp(ctx, ctrlClient, clusterName)
if err != nil {
if errors.IsNotFound(err) {
log.Error(err, "tbot: App not found", "app", app)
}
return err
}

appExtraConfigs := []v1alpha1.AppExtraConfig{}
if app.Spec.ExtraConfigs != nil {
appExtraConfigs = app.Spec.ExtraConfigs
}

extraConfig := t.getBotExtraConfig(clusterName)
app.Spec.ExtraConfigs = t.appendBotExtraConfig(appExtraConfigs, extraConfig)

if reflect.DeepEqual(appExtraConfigs, app.Spec.ExtraConfigs) {
return nil
}

log.Info("tbot: Updating app", "app", app)
if err := ctrlClient.Update(ctx, app); err != nil {
if errors.IsConflict(err) {
log.Error(err, "tbot: Conflict detected during app update", "app", app)
}

return microerror.Mask(err)
}

return nil
}

func (t *Teleport) DeleteBotAppExtraConfig(ctx context.Context, log logr.Logger, ctrlClient client.Client, clusterName string) error {
app, err := t.getBotApp(ctx, ctrlClient, clusterName)
if err != nil {
if errors.IsNotFound(err) {
log.Error(err, "tbot: App not found", "app", app)
}
return err
}

if app.Spec.ExtraConfigs == nil {
return nil
}
appExtraConfigs := app.Spec.ExtraConfigs

extraConfig := t.getBotExtraConfig(clusterName)
app.Spec.ExtraConfigs = t.removeBotExtraConfig(appExtraConfigs, extraConfig)

if reflect.DeepEqual(appExtraConfigs, app.Spec.ExtraConfigs) {
return nil
}

log.Info("tbot: Updating app", "app", app)
if err := ctrlClient.Update(ctx, app); err != nil {
if errors.IsConflict(err) {
log.Error(err, "tbot: Conflict detected during app update", "app", app)
}

return microerror.Mask(err)
}

return nil
}
78 changes: 78 additions & 0 deletions internal/pkg/teleport/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ func (t *Teleport) GetConfigMap(ctx context.Context, log logr.Logger, ctrlClient
return configMap, nil
}

func (t *Teleport) GetTbotConfigMap(ctx context.Context, ctrlClient client.Client, clusterName string) (*corev1.ConfigMap, error) {
var (
configMapName = key.GetTbotConfigmapName(clusterName)
configMap = &corev1.ConfigMap{}
)

key := client.ObjectKey{Name: configMapName, Namespace: key.TeleportBotNamespace}
if err := ctrlClient.Get(ctx, key, configMap); err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
}
return nil, microerror.Mask(fmt.Errorf("bot: Failed to get configmap: %w", err))
}

return configMap, nil
}

func (t *Teleport) GetTokenFromConfigMap(ctx context.Context, configMap *corev1.ConfigMap) (string, error) {
valuesBytes, ok := configMap.Data["values"]
if !ok {
Expand Down Expand Up @@ -83,6 +100,43 @@ func (t *Teleport) CreateConfigMap(ctx context.Context, log logr.Logger, ctrlCli
return nil
}

func (t *Teleport) CreateTbotConfigMap(ctx context.Context, ctrlClient client.Client, clusterName string, registerName string) (*corev1.ConfigMap, error) {
configMapName := key.GetTbotConfigmapName(clusterName)
data := map[string]string{
"values": t.getTbotConfigMapData(registerName, clusterName),
}
cm := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
Namespace: key.TeleportBotNamespace,
},
Data: data,
}

if err := ctrlClient.Create(ctx, &cm); err != nil {
return nil, microerror.Mask(err)
}

return &cm, nil
}

func (t *Teleport) EnsureTbotConfigMap(ctx context.Context, log logr.Logger, ctrlClient client.Client, clusterName string, namespace string, registerName string) error {
cm, err := t.GetTbotConfigMap(ctx, ctrlClient, clusterName)
if err != nil {
return microerror.Mask(err)
}

if cm == nil {
cm, err = t.CreateTbotConfigMap(ctx, ctrlClient, clusterName, registerName)
if err != nil {
return microerror.Mask(err)
}
log.Info("tbot: Created configmap", "configmap", cm)
}

return nil
}

func (t *Teleport) UpdateConfigMap(ctx context.Context, log logr.Logger, ctrlClient client.Client, configMap *corev1.ConfigMap, token string) error {
valuesBytes, ok := configMap.Data["values"]
if !ok {
Expand Down Expand Up @@ -131,6 +185,26 @@ func (t *Teleport) DeleteConfigMap(ctx context.Context, log logr.Logger, ctrlCli
return nil
}

func (t *Teleport) DeleteTbotConfigMap(ctx context.Context, log logr.Logger, ctrlClient client.Client, clusterName string, namespace string) error {
configMapName := key.GetTbotConfigmapName(clusterName)
cm := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
Namespace: namespace,
},
}

if err := ctrlClient.Delete(ctx, &cm); err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return microerror.Mask(err)
}

log.Info("tbot: Deleted configmap", "configMap", configMapName)
return nil
}

func (t *Teleport) getConfigMapData(registerName string, token string) string {
var (
authToken = token
Expand All @@ -141,3 +215,7 @@ func (t *Teleport) getConfigMapData(registerName string, token string) string {

return key.GetConfigmapDataFromTemplate(authToken, proxyAddr, kubeClusterName, teleportVersionOverride)
}

func (t *Teleport) getTbotConfigMapData(registerName string, clusterName string) string {
return key.GetTbotConfigmapDataFromTemplate(registerName, clusterName)
}
Loading

0 comments on commit 37386fd

Please sign in to comment.