Skip to content

Commit

Permalink
Added creation of job template which is responsible for creating the …
Browse files Browse the repository at this point in the history
…private and public key in provider/consumer mode.

Signed-off-by: rchikatw <[email protected]>
  • Loading branch information
rchikatw committed Dec 19, 2023
1 parent e8d575d commit 12cf646
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ ARG LDFLAGS

RUN GOOS="$GOOS" GOARCH="$GOARCH" go build -ldflags "$LDFLAGS" -tags netgo,osusergo -o ocs-operator main.go
RUN GOOS="$GOOS" GOARCH="$GOARCH" go build -tags netgo,osusergo -o provider-api services/provider/main.go
RUN GOOS="$GOOS" GOARCH="$GOARCH" go build -tags netgo,osusergo -o onboarding-secret-generator onboarding/main.go

# Build stage 2

FROM registry.access.redhat.com/ubi9/ubi-minimal

COPY --from=builder workspace/ocs-operator /usr/local/bin/ocs-operator
COPY --from=builder workspace/provider-api /usr/local/bin/provider-api
COPY --from=builder workspace/onboarding-secret-generator /usr/local/bin/onboarding-secret-generator
COPY --from=builder workspace/metrics/deploy/*rules*.yaml /ocs-prometheus-rules/

RUN chmod +x /usr/local/bin/ocs-operator /usr/local/bin/provider-api
Expand Down
76 changes: 74 additions & 2 deletions controllers/storagecluster/provider_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (

"go.uber.org/multierr"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand All @@ -24,8 +26,11 @@ import (
)

const (
ocsProviderServerName = "ocs-provider-server"
providerAPIServerImage = "PROVIDER_API_SERVER_IMAGE"
ocsProviderServerName = "ocs-provider-server"
providerAPIServerImage = "PROVIDER_API_SERVER_IMAGE"
onboardingSecretGeneratorImage = "ONBOARDING_SECRET_GENERATOR_IMAGE"
onboardingJobName = "onboarding-secret-generator"
onboardingTicketPublicKeySecretName = "onboarding-ticket-key"

ocsProviderServicePort = int32(50051)
ocsProviderServiceNodePort = int32(31659)
Expand Down Expand Up @@ -63,6 +68,12 @@ func (o *ocsProviderServer) ensureCreated(r *StorageClusterReconciler, instance
return res, nil
}

if res, err := o.createJob(r, instance); err != nil {
return reconcile.Result{}, err
} else if !res.IsZero() {
return res, nil
}

return reconcile.Result{}, nil
}

Expand Down Expand Up @@ -434,3 +445,64 @@ func RandomString(l int) string {

return string(bytes)
}

func getOnboardingJobObject(instance *ocsv1.StorageCluster) *batchv1.Job {

return &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: onboardingJobName,
Namespace: instance.Namespace,
},
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyOnFailure,
ServiceAccountName: onboardingJobName,
Containers: []corev1.Container{
{
Name: onboardingJobName,
Image: os.Getenv(onboardingSecretGeneratorImage),
Command: []string{"/usr/local/bin/onboarding-secret-generator"},
Env: []corev1.EnvVar{
{
Name: util.OperatorNamespaceEnvVar,
Value: os.Getenv(util.OperatorNamespaceEnvVar),
},
},
},
},
},
},
},
}
}

func (o *ocsProviderServer) createJob(r *StorageClusterReconciler, instance *ocsv1.StorageCluster) (reconcile.Result, error) {
if os.Getenv(onboardingSecretGeneratorImage) == "" {
err := fmt.Errorf("onboardingSecretGeneratorImage env var is not set.")

Check failure on line 482 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.20)

error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)

Check warning on line 482 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.20)

error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)

Check failure on line 482 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.21)

error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)

Check warning on line 482 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.21)

error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
r.Log.Error(err, "No value for env variable", onboardingSecretGeneratorImage)
return reconcile.Result{}, err
} else {

Check failure on line 485 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.20)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)

Check warning on line 485 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.20)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)

Check failure on line 485 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.21)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)

Check warning on line 485 in controllers/storagecluster/provider_server.go

View workflow job for this annotation

GitHub Actions / golangci-lint (1.21)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)

actualSecret := &corev1.Secret{}
// Creating the job only if public is not found
err := r.Client.Get(context.Background(), types.NamespacedName{Name: onboardingTicketPublicKeySecretName,
Namespace: instance.Namespace}, actualSecret)

if err != nil && errors.IsNotFound(err) {
onboardingSecretGeneratorJob := getOnboardingJobObject(instance)
err = r.Client.Create(context.Background(), onboardingSecretGeneratorJob)
if err != nil {
r.Log.Error(err, "Failed to create job.")
return reconcile.Result{}, err
}
}
if err != nil {
r.Log.Error(err, "failed to ensure secret")
return reconcile.Result{}, err
}

}
r.Log.Info("Job is running as desired")
return reconcile.Result{}, nil
}
13 changes: 13 additions & 0 deletions controllers/storagecluster/storagecluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ func (r *StorageClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
},
)

onboardingSecretPredicates := builder.WithPredicates(
predicate.NewPredicateFuncs(
func(client client.Object) bool {
return client.GetName() == onboardingTicketPublicKeySecretName
},
),
)

builder := ctrl.NewControllerManagedBy(mgr).
For(&ocsv1.StorageCluster{}, builder.WithPredicates(scPredicate)).
Owns(&cephv1.CephCluster{}).
Expand All @@ -199,6 +207,11 @@ func (r *StorageClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
},
},
enqueueStorageClusterRequest,
).
Watches(
&corev1.Secret{},
enqueueStorageClusterRequest,
onboardingSecretPredicates,
)
if os.Getenv("SKIP_NOOBAA_CRD_WATCH") != "true" {
builder.Owns(&nbv1.NooBaa{})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3077,6 +3077,8 @@ spec:
value: docker.io/centos/postgresql-12-centos8
- name: PROVIDER_API_SERVER_IMAGE
value: quay.io/ocs-dev/ocs-operator:latest
- name: ONBOARDING_SECRET_GENERATOR_IMAGE
value: quay.io/ocs-dev/ocs-operator:latest
- name: OPERATOR_NAMESPACE
valueFrom:
fieldRef:
Expand Down
2 changes: 1 addition & 1 deletion hack/source-manifests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function gen_ocs_csv() {
pushd config/manager
$KUSTOMIZE edit set image ocs-dev/ocs-operator="$OCS_IMAGE"
popd
$KUSTOMIZE build config/manifests/ocs-operator | $OPERATOR_SDK generate bundle -q --overwrite=false --output-dir deploy/ocs-operator --kustomize-dir config/manifests/ocs-operator --package ocs-operator --version "$CSV_VERSION"
$KUSTOMIZE build config/manifests/ocs-operator | $OPERATOR_SDK generate bundle -q --overwrite=false --output-dir deploy/ocs-operator --kustomize-dir config/manifests/ocs-operator --package ocs-operator --version "$CSV_VERSION" --extra-service-accounts=onboarding-secret-generator
mv deploy/ocs-operator/manifests/*clusterserviceversion.yaml $OCS_CSV
cp config/crd/bases/* $ocs_crds_outdir
}
Expand Down
124 changes: 124 additions & 0 deletions onboarding/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"os"

"github.com/red-hat-storage/ocs-operator/v4/controllers/util"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
runtime "sigs.k8s.io/controller-runtime/pkg/client/config"
)

const (
onboardingTicketPublicKeySecretName = "onboarding-ticket-key" //Name of existing public key which is used ocs-operator
onboardingTicketPrivateKeySecretName = "onboarding-ticket-private-key"
serviceAccountName = "onboarding-secret-generator"
)

func main() {
clientset, err := newClient()
if err != nil {
klog.Error(err, "failed to create controller-runtime client")
return
}

operatorNamespace, err := util.GetOperatorNamespace()
if err != nil {
klog.Error(err, "unable to get operator namespace")
os.Exit(1)
}

// 1. Check public key secret exist or not
_, err = clientset.CoreV1().Secrets(operatorNamespace).Get(context.TODO(), onboardingTicketPublicKeySecretName, metav1.GetOptions{})

if err != nil && kerrors.IsNotFound(err) {
// Generate RSA key.
var err error
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
klog.Error(err, "unable to generate private")
os.Exit(1)
}

publicKey := &privateKey.PublicKey
// Export the keys to pem string
privatePem := convertRsaPrivateKeyAsPemStr(privateKey)
publicPem, err := convertRsaPublicKeyAsPemStr(publicKey)

if err != nil {
klog.Error(err, "failed to convert public key to pem str")
os.Exit(1)
}

privateSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: onboardingTicketPrivateKeySecretName,
Namespace: operatorNamespace,
Annotations: map[string]string{"kubernetes.io/service-account.name": serviceAccountName},
},
Type: "kubernetes.io/service-account-token",
StringData: map[string]string{
"key": privatePem,
},
}

_, err = clientset.CoreV1().Secrets(operatorNamespace).Create(context.Background(), privateSecret, metav1.CreateOptions{})

if err != nil {
klog.Error(err, "Failed to create private secret.")
os.Exit(1)
}
publicSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: onboardingTicketPublicKeySecretName,
Namespace: operatorNamespace,
},
StringData: map[string]string{
"key": publicPem,
},
}

_, err = clientset.CoreV1().Secrets(operatorNamespace).Create(context.Background(), publicSecret, metav1.CreateOptions{})
if err != nil {
klog.Error(err, "Failed to create public secret.")
os.Exit(1)
}

}

}

func newClient() (*kubernetes.Clientset, error) {
config := runtime.GetConfigOrDie()
clientset, err := kubernetes.NewForConfig(config)

if err != nil {
klog.Error(err, "failed to get clientset")
}

return clientset, nil
}

func convertRsaPrivateKeyAsPemStr(privateKey *rsa.PrivateKey) string {
privteKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateKeyPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privteKeyBytes})
return string(privateKeyPem)
}

func convertRsaPublicKeyAsPemStr(publicKey *rsa.PublicKey) (string, error) {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return "", err
}
publicKeyPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: publicKeyBytes})

return string(publicKeyPem), nil
}
12 changes: 12 additions & 0 deletions rbac/onboarding-secret-generator-binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: onboarding-secret-generator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: onboarding-secret-generator
subjects:
- kind: ServiceAccount
name: onboarding-secret-generator
namespace: openshift-storage
13 changes: 13 additions & 0 deletions rbac/onboarding-secret-generator-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: onboarding-secret-generator
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- create
5 changes: 5 additions & 0 deletions rbac/onboarding-secret-generator-sa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: ServiceAccount
apiVersion: v1
metadata:
name: onboarding-secret-generator
type: kubernetes.io/service-account-token
4 changes: 4 additions & 0 deletions tools/csv-merger/csv-merger.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ func unmarshalCSV(filePath string) *csvv1.ClusterServiceVersion {
Name: "PROVIDER_API_SERVER_IMAGE",
Value: *ocsContainerImage,
},
{
Name: "ONBOARDING_SECRET_GENERATOR_IMAGE",
Value: *ocsContainerImage,
},
{
Name: util.OperatorNamespaceEnvVar,
ValueFrom: &corev1.EnvVarSource{
Expand Down

0 comments on commit 12cf646

Please sign in to comment.