Skip to content

Commit

Permalink
create StorageCluster peer token secret on the hub
Browse files Browse the repository at this point in the history
Signed-off-by: Umanga Chapagain <[email protected]>
  • Loading branch information
umangachapagain committed Jul 25, 2024
1 parent 7cfca25 commit 4dc2459
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 1 deletion.
48 changes: 48 additions & 0 deletions addons/agent_mirrorpeer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"log/slog"
"strconv"
Expand Down Expand Up @@ -156,6 +157,53 @@ func (r *MirrorPeerReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, fmt.Errorf("few failures occurred while labeling RBD StorageClasses: %v", errs)
}
}

if mirrorPeer.Spec.Type == multiclusterv1alpha1.Async {
if mirrorPeer.Status.Phase == multiclusterv1alpha1.ExchangedSecret {
logger.Info("Cleaning up stale onboarding token", "Token", string(mirrorPeer.GetUID()))
err = deleteStorageClusterPeerTokenSecret(ctx, r.HubClient, r.SpokeClusterName, string(mirrorPeer.GetUID()))
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
if mirrorPeer.Status.Phase == multiclusterv1alpha1.ExchangingSecret {
var token corev1.Secret
err = r.HubClient.Get(ctx, types.NamespacedName{Namespace: r.SpokeClusterName, Name: string(mirrorPeer.GetUID())}, &token)
if err != nil && !errors.IsNotFound(err) {
return ctrl.Result{}, err
}
if err == nil {
// TODO: Replace it with exported type from ocs-operator
type OnboardingTicket struct {
ID string `json:"id"`
ExpirationDate int64 `json:"expirationDate,string"`
StorageQuotaInGiB uint `json:"storageQuotaInGiB,omitempty"`
}
var ticketData OnboardingTicket
err = json.Unmarshal(token.Data["storagecluster-peer-token"], &ticketData)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to unmarshal onboarding ticket message. %w", err)
}
if ticketData.ExpirationDate > time.Now().Unix() {
logger.Info("Onboarding token has not expired yet. Not renewing it.", "Token", token.Name, "ExpirationDate", ticketData.ExpirationDate)
return ctrl.Result{}, nil
}
logger.Info("Onboarding token has expired. Deleting it", "Token", token.Name)
err = deleteStorageClusterPeerTokenSecret(ctx, r.HubClient, r.SpokeClusterName, string(mirrorPeer.GetUID()))
if err != nil {
return ctrl.Result{}, err
}
}
logger.Info("Creating a new onboarding token", "Token", token.Name)
err = createStorageClusterPeerTokenSecret(ctx, r.HubClient, r.Scheme, r.SpokeClusterName, "openshift-storage", mirrorPeer, scr) //TODO: get odfOperatorNamespace from addon flags
if err != nil {
logger.Error("Failed to create StorageCluster peer token on the hub.", "error", err)
return ctrl.Result{}, err
}
}
}

return ctrl.Result{}, nil
}

Expand Down
116 changes: 116 additions & 0 deletions addons/onboarding_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package addons

import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
"time"

"github.com/red-hat-storage/odf-multicluster-orchestrator/addons/setup"
"github.com/red-hat-storage/odf-multicluster-orchestrator/api/v1alpha1"
multiclusterv1alpha1 "github.com/red-hat-storage/odf-multicluster-orchestrator/api/v1alpha1"
"github.com/red-hat-storage/odf-multicluster-orchestrator/controllers/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

func requestStorageClusterPeerToken(ctx context.Context, proxyServiceNamespace string) (string, error) {
token, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return "", fmt.Errorf("failed to read token: %w", err)
}
url := fmt.Sprintf("https://ux-backend-proxy.%s.svc.cluster.local:8888/onboarding-tokens", proxyServiceNamespace)
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}

req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
if err != nil {
return "", fmt.Errorf("failed to create http request: %w", err)
}

req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(token)))

resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("http request failed: %w", err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read http response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unexpected status code: %s", http.StatusText(resp.StatusCode))
}

return string(body), nil
}

func createStorageClusterPeerTokenSecret(ctx context.Context, client client.Client, scheme *runtime.Scheme, spokeClusterName string, odfOperatorNamespace string, mirrorPeer multiclusterv1alpha1.MirrorPeer, storageClusterRef *v1alpha1.StorageClusterRef) error {
uniqueSecretName := string(mirrorPeer.GetUID())
_, err := utils.FetchSecretWithName(ctx, client, types.NamespacedName{Namespace: spokeClusterName, Name: uniqueSecretName})
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("failed to get secret %s/%s: %w", spokeClusterName, uniqueSecretName, err)
}
if err == nil {
return errors.NewAlreadyExists(corev1.Resource("secret"), uniqueSecretName)
}

token, err := requestStorageClusterPeerToken(ctx, odfOperatorNamespace)
if err != nil {
return fmt.Errorf("unable to generate StorageClusterPeer token. %w", err)
}

tokenSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: uniqueSecretName,
Namespace: spokeClusterName,
Labels: map[string]string{
utils.CreatedByLabelKey: setup.TokenExchangeName,
utils.SecretLabelTypeKey: string(utils.InternalLabel),
utils.HubRecoveryLabel: "",
},
},
Data: map[string][]byte{
utils.NamespaceKey: []byte(storageClusterRef.Namespace),
utils.StorageClusterNameKey: []byte(storageClusterRef.Name),
utils.SecretDataKey: []byte(token),
},
}

err = controllerutil.SetOwnerReference(&mirrorPeer, tokenSecret, scheme)
if err != nil {
return fmt.Errorf("failed to set owner reference for secret %s/%s: %w", spokeClusterName, uniqueSecretName, err)
}

return client.Create(ctx, tokenSecret)
}

func deleteStorageClusterPeerTokenSecret(ctx context.Context, client client.Client, tokenNamespace string, tokenName string) error {
token := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: tokenName,
Namespace: tokenNamespace,
},
}

err := client.Delete(ctx, token)
if err != nil && !errors.IsNotFound(err) {
return err
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ rules:
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["ocs.openshift.io"]
resources: ["storageclusters"]
verbs: ["get", "list", "watch", "update"]
verbs: ["get", "list", "watch", "create", "update"]
- apiGroups: ["objectbucket.io"]
resources: ["objectbucketclaims"]
verbs: ["get", "create", "list", "watch", "delete"]
Expand Down

0 comments on commit 4dc2459

Please sign in to comment.