Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a service account for pull secrets #579

Merged
merged 10 commits into from
Jul 8, 2020
1 change: 0 additions & 1 deletion manageiq-operator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/ManageIQ/manageiq-pods/manageiq-operator
go 1.14

require (
github.com/google/uuid v1.1.1
github.com/operator-framework/operator-sdk v0.15.1
github.com/spf13/pflag v1.0.5
k8s.io/api v0.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
miqtool "github.com/ManageIQ/manageiq-pods/manageiq-operator/pkg/helpers/miq-components"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -118,6 +117,9 @@ func (r *ReconcileManageIQ) Reconcile(request reconcile.Request) (reconcile.Resu
if e := r.generateSecrets(miqInstance); e != nil {
return reconcile.Result{}, e
}
if e := r.generateDefaultServiceAccount(miqInstance); e != nil {
return reconcile.Result{}, e
}
if e := r.generatePostgresqlResources(miqInstance); e != nil {
return reconcile.Result{}, e
}
Expand All @@ -139,6 +141,17 @@ func (r *ReconcileManageIQ) Reconcile(request reconcile.Request) (reconcile.Resu
return reconcile.Result{}, nil
}

func (r *ReconcileManageIQ) generateDefaultServiceAccount(cr *miqv1alpha1.ManageIQ) error {
serviceAccount, mutateFunc := miqtool.DefaultServiceAccount(cr, r.scheme)
if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, serviceAccount, mutateFunc); err != nil {
return err
} else if result != controllerutil.OperationResultNone {
logger.Info("Service Account has been reconciled", "component", "app", "result", result)
}

return nil
}

func (r *ReconcileManageIQ) generateHttpdResources(cr *miqv1alpha1.ManageIQ) error {
privileged, err := miqtool.PrivilegedHttpd(cr.Spec.HttpdAuthenticationType)
if err != nil {
Expand Down Expand Up @@ -351,27 +364,33 @@ func (r *ReconcileManageIQ) generateKafkaResources(cr *miqv1alpha1.ManageIQ) err
}

func (r *ReconcileManageIQ) generateOrchestratorResources(cr *miqv1alpha1.ManageIQ) error {
orchestratorServiceAccount := miqtool.OrchestratorServiceAccount(cr)
if err := r.createk8sResIfNotExist(cr, orchestratorServiceAccount, &corev1.ServiceAccount{}); err != nil {
serviceAccount, mutateFunc := miqtool.OrchestratorServiceAccount(cr, r.scheme)
if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, serviceAccount, mutateFunc); err != nil {
return err
} else if result != controllerutil.OperationResultNone {
logger.Info("Service Account has been reconciled", "component", "orchestrator", "result", result)
}

orchestratorRole := miqtool.OrchestratorRole(cr)
if err := r.createk8sResIfNotExist(cr, orchestratorRole, &rbacv1.Role{}); err != nil {
role, mutateFunc := miqtool.OrchestratorRole(cr, r.scheme)
if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, role, mutateFunc); err != nil {
return err
} else if result != controllerutil.OperationResultNone {
logger.Info("Role has been reconciled", "component", "orchestrator", "result", result)
}

orchestratorRoleBinding := miqtool.OrchestratorRoleBinding(cr)
if err := r.createk8sResIfNotExist(cr, orchestratorRoleBinding, &rbacv1.RoleBinding{}); err != nil {
roleBinding, mutateFunc := miqtool.OrchestratorRoleBinding(cr, r.scheme)
if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, roleBinding, mutateFunc); err != nil {
return err
} else if result != controllerutil.OperationResultNone {
logger.Info("Role Binding has been reconciled", "component", "orchestrator", "result", result)
}

orchestratorDeployment, mutateFunc, err := miqtool.OrchestratorDeployment(cr, r.scheme)
deployment, mutateFunc, err := miqtool.OrchestratorDeployment(cr, r.scheme)
if err != nil {
return err
}

if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, orchestratorDeployment, mutateFunc); err != nil {
if result, err := controllerutil.CreateOrUpdate(context.TODO(), r.client, deployment, mutateFunc); err != nil {
return err
} else if result != controllerutil.OperationResultNone {
logger.Info("Deployment has been reconciled", "component", "orchestrator", "result", result)
Expand Down
7 changes: 7 additions & 0 deletions manageiq-operator/pkg/helpers/miq-components/httpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func HttpdServiceAccount(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*cor
if err := controllerutil.SetControllerReference(cr, serviceAccount, scheme); err != nil {
return err
}

if cr.Spec.ImagePullSecret != "" {
addSAPullSecret(serviceAccount, cr.Spec.ImagePullSecret)
}

return nil
}

Expand Down Expand Up @@ -359,6 +364,8 @@ func HttpdDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*appsv1.
// Only assign the service account if we need additional privileges
if privileged {
deployment.Spec.Template.Spec.ServiceAccountName = cr.Spec.AppName + "-httpd"
} else {
deployment.Spec.Template.Spec.ServiceAccountName = defaultServiceAccountName(cr.Spec.AppName)
}

configureHttpdAuth(&cr.Spec, &deployment.Spec.Template.Spec)
Expand Down
2 changes: 2 additions & 0 deletions manageiq-operator/pkg/helpers/miq-components/kafka.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ func KafkaDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*appsv1.
var repNum int32 = 1
deployment.Spec.Replicas = &repNum
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
deployment.Spec.Template.Spec.ServiceAccountName = defaultServiceAccountName(cr.Spec.AppName)
var termSecs int64 = 10
deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = &termSecs
deployment.Spec.Template.Spec.Volumes = []corev1.Volume{
Expand Down Expand Up @@ -323,6 +324,7 @@ func ZookeeperDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*app
var repNum int32 = 1
deployment.Spec.Replicas = &repNum
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
deployment.Spec.Template.Spec.ServiceAccountName = defaultServiceAccountName(cr.Spec.AppName)
deployment.Spec.Template.Spec.Volumes = []corev1.Volume{
corev1.Volume{
Name: "zookeeper-data",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func NewMemcachedDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*
var repNum int32 = 1
deployment.Spec.Replicas = &repNum
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
deployment.Spec.Template.Spec.ServiceAccountName = defaultServiceAccountName(cr.Spec.AppName)
return nil
}

Expand Down
78 changes: 52 additions & 26 deletions manageiq-operator/pkg/helpers/miq-components/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,43 @@ import (
"strings"
)

func OrchestratorServiceAccount(cr *miqv1alpha1.ManageIQ) *corev1.ServiceAccount {
return &corev1.ServiceAccount{
func OrchestratorServiceAccount(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*corev1.ServiceAccount, controllerutil.MutateFn) {
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: orchestratorObjectName(cr),
Namespace: cr.ObjectMeta.Namespace,
},
}

f := func() error {
if err := controllerutil.SetControllerReference(cr, sa, scheme); err != nil {
return err
}

if cr.Spec.ImagePullSecret != "" {
addSAPullSecret(sa, cr.Spec.ImagePullSecret)
}

return nil
}

return sa, f
}

func OrchestratorRole(cr *miqv1alpha1.ManageIQ) *rbacv1.Role {
return &rbacv1.Role{
func OrchestratorRole(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*rbacv1.Role, controllerutil.MutateFn) {
role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: orchestratorObjectName(cr),
Namespace: cr.ObjectMeta.Namespace,
},
Rules: []rbacv1.PolicyRule{
}

f := func() error {
if err := controllerutil.SetControllerReference(cr, role, scheme); err != nil {
return err
}

role.Rules = []rbacv1.PolicyRule{
rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"pods", "pods/finalizers"},
Expand All @@ -43,28 +64,43 @@ func OrchestratorRole(cr *miqv1alpha1.ManageIQ) *rbacv1.Role {
Resources: []string{"deployments", "deployments/scale"},
Verbs: []string{"*"},
},
},
}

return nil
}

return role, f
}

func OrchestratorRoleBinding(cr *miqv1alpha1.ManageIQ) *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
func OrchestratorRoleBinding(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*rbacv1.RoleBinding, controllerutil.MutateFn) {
rb := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: orchestratorObjectName(cr),
Namespace: cr.ObjectMeta.Namespace,
},
RoleRef: rbacv1.RoleRef{
}

f := func() error {
if err := controllerutil.SetControllerReference(cr, rb, scheme); err != nil {
return err
}

rb.RoleRef = rbacv1.RoleRef{
Kind: "Role",
Name: orchestratorObjectName(cr),
APIGroup: "rbac.authorization.k8s.io",
},
Subjects: []rbacv1.Subject{
}
rb.Subjects = []rbacv1.Subject{
rbacv1.Subject{
Kind: "ServiceAccount",
Name: orchestratorObjectName(cr),
},
},
}

return nil
}

return rb, f
}

func orchestratorObjectName(cr *miqv1alpha1.ManageIQ) string {
Expand Down Expand Up @@ -270,6 +306,10 @@ func OrchestratorDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*
Name: "WORKER_RESOURCES",
Value: strconv.FormatBool(*cr.Spec.EnforceWorkerResourceConstraints),
},
corev1.EnvVar{
Name: "WORKER_SERVICE_ACCOUNT",
Value: defaultServiceAccountName(cr.Spec.AppName),
},
},
}

Expand Down Expand Up @@ -311,20 +351,6 @@ func OrchestratorDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*
deployment.Spec.Template.Spec.ServiceAccountName = cr.Spec.AppName + "-orchestrator"
deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = &termSecs

if cr.Spec.ImagePullSecret != "" {
pullSecret := []corev1.LocalObjectReference{
corev1.LocalObjectReference{Name: cr.Spec.ImagePullSecret},
}
deployment.Spec.Template.Spec.ImagePullSecrets = pullSecret

c := &deployment.Spec.Template.Spec.Containers[0]
pullSecretEnv := corev1.EnvVar{
Name: "IMAGE_PULL_SECRET",
Value: cr.Spec.ImagePullSecret,
}
c.Env = append(c.Env, pullSecretEnv)
}

return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ func PostgresqlDeployment(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*ap
var repNum int32 = 1
deployment.Spec.Replicas = &repNum
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
deployment.Spec.Template.Spec.ServiceAccountName = defaultServiceAccountName(cr.Spec.AppName)
deployment.Spec.Template.Spec.Volumes = []corev1.Volume{
corev1.Volume{
Name: "miq-pgdb-volume",
Expand Down
51 changes: 51 additions & 0 deletions manageiq-operator/pkg/helpers/miq-components/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package miqtools

import (
"fmt"
miqv1alpha1 "github.com/ManageIQ/manageiq-pods/manageiq-operator/pkg/apis/manageiq/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

func addSAPullSecret(sa *corev1.ServiceAccount, secret string) {
secretRef := corev1.LocalObjectReference{Name: secret}
if sa.ImagePullSecrets == nil {
sa.ImagePullSecrets = []corev1.LocalObjectReference{secretRef}
} else {
for _, ref := range sa.ImagePullSecrets {
if ref.Name == secret {
return
}
}
sa.ImagePullSecrets = append(sa.ImagePullSecrets, secretRef)
}
}

func defaultServiceAccountName(appName string) string {
return fmt.Sprintf("%s-default", appName)
}

func DefaultServiceAccount(cr *miqv1alpha1.ManageIQ, scheme *runtime.Scheme) (*corev1.ServiceAccount, controllerutil.MutateFn) {
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: defaultServiceAccountName(cr.Spec.AppName),
Namespace: cr.ObjectMeta.Namespace,
},
}

f := func() error {
if err := controllerutil.SetControllerReference(cr, sa, scheme); err != nil {
return err
}

if cr.Spec.ImagePullSecret != "" {
addSAPullSecret(sa, cr.Spec.ImagePullSecret)
}

return nil
}

return sa, f
}