Automate the setup of github identity provider with internal keycloak (#589)
* GitHub identity provider provision Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/631/head
parent
5b78d6b545
commit
482155b7e2
|
|
@ -99,7 +99,7 @@
|
|||
"stable",
|
||||
"che"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "bashdb",
|
||||
"request": "launch",
|
||||
|
|
@ -145,7 +145,8 @@
|
|||
"mode": "test",
|
||||
"program": "${file}",
|
||||
"env": {
|
||||
"MOCK_API": true
|
||||
"MOCK_API": true,
|
||||
"CHE_FLAVOR": "che"
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ RUN export ARCH="$(uname -m)" && if [[ ${ARCH} == "x86_64" ]]; then export ARCH=
|
|||
FROM registry.access.redhat.com/ubi8-minimal:8.3-230
|
||||
|
||||
COPY --from=builder /tmp/che-operator/che-operator /usr/local/bin/che-operator
|
||||
COPY --from=builder /che-operator/templates/keycloak_provision /tmp/keycloak_provision
|
||||
COPY --from=builder /che-operator/templates/oauth_provision /tmp/oauth_provision
|
||||
COPY --from=builder /che-operator/templates/keycloak-provision.sh /tmp/keycloak-provision.sh
|
||||
COPY --from=builder /che-operator/templates/oauth-provision.sh /tmp/oauth-provision.sh
|
||||
COPY --from=builder /che-operator/templates/delete-identity-provider.sh /tmp/delete-identity-provider.sh
|
||||
COPY --from=builder /che-operator/templates/create-github-identity-provider.sh /tmp/create-github-identity-provider.sh
|
||||
# apply CVE fixes, if required
|
||||
RUN microdnf update -y librepo libnghttp2 && microdnf clean all && rm -rf /var/cache/yum && echo "Installed Packages" && rpm -qa | sort -V && echo "End Of Installed Packages"
|
||||
CMD ["che-operator"]
|
||||
|
|
|
|||
|
|
@ -696,6 +696,10 @@ spec:
|
|||
devfileRegistryURL:
|
||||
description: Public URL to the Devfile registry
|
||||
type: string
|
||||
gitHubOAuthProvisioned:
|
||||
description: Indicates whether an Identity Provider instance (Keycloak
|
||||
/ RH SSO) has been configured to integrate with the GitHub OAuth.
|
||||
type: boolean
|
||||
helpLink:
|
||||
description: A URL that can point to some URL where to find help related
|
||||
to the current Operator status.
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ metadata:
|
|||
categories: Developer Tools
|
||||
certified: "false"
|
||||
containerImage: quay.io/eclipse/che-operator:nightly
|
||||
createdAt: "2021-01-08T14:09:35Z"
|
||||
createdAt: "2021-01-13T13:41:43Z"
|
||||
description: A Kube-native development solution that delivers portable and collaborative
|
||||
developer workspaces.
|
||||
operatorframework.io/suggested-namespace: eclipse-che
|
||||
repository: https://github.com/eclipse/che-operator
|
||||
support: Eclipse Foundation
|
||||
name: eclipse-che-preview-kubernetes.v7.25.0-66.nightly
|
||||
name: eclipse-che-preview-kubernetes.v7.25.0-68.nightly
|
||||
namespace: placeholder
|
||||
spec:
|
||||
apiservicedefinitions: {}
|
||||
|
|
@ -514,4 +514,4 @@ spec:
|
|||
maturity: stable
|
||||
provider:
|
||||
name: Eclipse Foundation
|
||||
version: 7.25.0-66.nightly
|
||||
version: 7.25.0-68.nightly
|
||||
|
|
|
|||
|
|
@ -696,6 +696,10 @@ spec:
|
|||
devfileRegistryURL:
|
||||
description: Public URL to the Devfile registry
|
||||
type: string
|
||||
gitHubOAuthProvisioned:
|
||||
description: Indicates whether an Identity Provider instance (Keycloak
|
||||
/ RH SSO) has been configured to integrate with the GitHub OAuth.
|
||||
type: boolean
|
||||
helpLink:
|
||||
description: A URL that can point to some URL where to find help related
|
||||
to the current Operator status.
|
||||
|
|
|
|||
|
|
@ -75,13 +75,13 @@ metadata:
|
|||
categories: Developer Tools, OpenShift Optional
|
||||
certified: "false"
|
||||
containerImage: quay.io/eclipse/che-operator:nightly
|
||||
createdAt: "2021-01-08T14:09:42Z"
|
||||
createdAt: "2021-01-13T13:41:52Z"
|
||||
description: A Kube-native development solution that delivers portable and collaborative
|
||||
developer workspaces in OpenShift.
|
||||
operatorframework.io/suggested-namespace: eclipse-che
|
||||
repository: https://github.com/eclipse/che-operator
|
||||
support: Eclipse Foundation
|
||||
name: eclipse-che-preview-openshift.v7.25.0-66.nightly
|
||||
name: eclipse-che-preview-openshift.v7.25.0-68.nightly
|
||||
namespace: placeholder
|
||||
spec:
|
||||
apiservicedefinitions: {}
|
||||
|
|
@ -533,4 +533,4 @@ spec:
|
|||
maturity: stable
|
||||
provider:
|
||||
name: Eclipse Foundation
|
||||
version: 7.25.0-66.nightly
|
||||
version: 7.25.0-68.nightly
|
||||
|
|
|
|||
|
|
@ -697,6 +697,10 @@ spec:
|
|||
devfileRegistryURL:
|
||||
description: Public URL to the Devfile registry
|
||||
type: string
|
||||
gitHubOAuthProvisioned:
|
||||
description: Indicates whether an Identity Provider instance (Keycloak
|
||||
/ RH SSO) has been configured to integrate with the GitHub OAuth.
|
||||
type: boolean
|
||||
helpLink:
|
||||
description: A URL that can point to some URL where to find help related
|
||||
to the current Operator status.
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ echo "[INFO] CR file path: ${CR}"
|
|||
|
||||
kubectl apply -f deploy/crds/org_v1_che_crd.yaml
|
||||
kubectl apply -f "${CR}" -n $CHE_NAMESPACE
|
||||
cp templates/keycloak_provision /tmp/keycloak_provision
|
||||
cp templates/oauth_provision /tmp/oauth_provision
|
||||
cp templates/keycloak-provision.sh /tmp/keycloak-provision.sh
|
||||
cp templates/delete-identity-provider.sh /tmp/delete-identity-provider.sh
|
||||
cp templates/create-github-identity-provider.sh /tmp/create-github-identity-provider.sh
|
||||
|
||||
ENV_FILE=/tmp/che-operator-debug.env
|
||||
rm -rf "${ENV_FILE}"
|
||||
|
|
@ -72,7 +73,7 @@ echo "WATCH_NAMESPACE='${CHE_NAMESPACE}'" >> ${ENV_FILE}
|
|||
|
||||
echo "[WARN] Make sure that your CR contains valid ingress domain!"
|
||||
|
||||
operator-sdk run --local --namespace ${CHE_NAMESPACE} --enable-delve &
|
||||
operator-sdk run --local --watch-namespace ${CHE_NAMESPACE} --enable-delve &
|
||||
OPERATOR_SDK_PID=$!
|
||||
|
||||
wait ${OPERATOR_SDK_PID}
|
||||
|
|
|
|||
|
|
@ -553,6 +553,9 @@ type CheClusterStatus struct {
|
|||
// Indicates whether an Identity Provider instance (Keycloak / RH SSO) has been configured to integrate with the OpenShift OAuth.
|
||||
// +optional
|
||||
OpenShiftoAuthProvisioned bool `json:"openShiftoAuthProvisioned"`
|
||||
// Indicates whether an Identity Provider instance (Keycloak / RH SSO) has been configured to integrate with the GitHub OAuth.
|
||||
// +optional
|
||||
GitHubOAuthProvisioned bool `json:"gitHubOAuthProvisioned"`
|
||||
// Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`
|
||||
// +optional
|
||||
// +operator-sdk:gen-csv:customresourcedefinitions.statusDescriptors=true
|
||||
|
|
|
|||
|
|
@ -718,15 +718,15 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
|
||||
// Create a new Postgres deployment
|
||||
deploymentStatus := postgres.SyncPostgresDeploymentToCluster(deployContext)
|
||||
provisioned, err := postgres.SyncPostgresDeploymentToCluster(deployContext)
|
||||
if !tests {
|
||||
if !deploymentStatus.Continue {
|
||||
if !provisioned {
|
||||
logrus.Infof("Waiting on deployment '%s' to be ready", postgres.PostgresDeploymentName)
|
||||
if deploymentStatus.Err != nil {
|
||||
logrus.Error(deploymentStatus.Err)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
return reconcile.Result{Requeue: deploymentStatus.Requeue}, deploymentStatus.Err
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -741,15 +741,16 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
identityProviderPostgresPassword = password
|
||||
}
|
||||
pgCommand := identity_provider.GetPostgresProvisionCommand(identityProviderPostgresPassword)
|
||||
dbStatus := instance.Status.DbProvisoned
|
||||
// provision Db and users for Che and Keycloak servers
|
||||
if !dbStatus {
|
||||
podToExec, err := util.K8sclient.GetDeploymentPod(postgres.PostgresDeploymentName, instance.Namespace)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
_, err = util.K8sclient.ExecIntoPod(podToExec, pgCommand, "create Keycloak DB, user, privileges", instance.Namespace)
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
instance,
|
||||
postgres.PostgresDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return identity_provider.GetPostgresProvisionCommand(identityProviderPostgresPassword), nil
|
||||
},
|
||||
"create Keycloak DB, user, privileges")
|
||||
if err == nil {
|
||||
for {
|
||||
instance.Status.DbProvisoned = true
|
||||
|
|
@ -842,7 +843,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
if err != nil {
|
||||
logrus.Errorf("Error provisioning the identity provider to cluster: %v", err)
|
||||
}
|
||||
return reconcile.Result{RequeueAfter: time.Second * 1}, err
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -886,12 +887,12 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
|
||||
// Create a new che deployment
|
||||
deploymentStatus := server.SyncCheDeploymentToCluster(deployContext)
|
||||
provisioned, err = server.SyncCheDeploymentToCluster(deployContext)
|
||||
if !tests {
|
||||
if !deploymentStatus.Continue {
|
||||
if !provisioned {
|
||||
logrus.Infof("Waiting on deployment '%s' to be ready", cheFlavor)
|
||||
if deploymentStatus.Err != nil {
|
||||
logrus.Error(deploymentStatus.Err)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
deployment, err := r.GetEffectiveDeployment(instance, cheFlavor)
|
||||
|
|
@ -912,7 +913,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
}
|
||||
}
|
||||
return reconcile.Result{Requeue: deploymentStatus.Requeue}, deploymentStatus.Err
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
// Update available status
|
||||
|
|
|
|||
|
|
@ -805,7 +805,7 @@ func TestCheController(t *testing.T) {
|
|||
if err = r.client.Get(context.TODO(), types.NamespacedName{Name: cheCR.Name, Namespace: cheCR.Namespace}, cheCR); err != nil {
|
||||
t.Errorf("Failed to get the Che custom resource %s: %s", cheCR.Name, err)
|
||||
}
|
||||
if err = identity_provider.SyncIdentityProviderItems(deployContext, "che"); err != nil {
|
||||
if _, err = identity_provider.SyncOpenShiftIdentityProviderItems(deployContext); err != nil {
|
||||
t.Errorf("Failed to create the items for the identity provider: %s", err)
|
||||
}
|
||||
oAuthClientName := cheCR.Spec.Auth.OAuthClientName
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ package che
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
identity_provider "github.com/eclipse/che-operator/pkg/deploy/identity-provider"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
|
|
@ -51,12 +52,18 @@ func (r *ReconcileChe) ReconcileIdentityProvider(instance *orgv1.CheCluster, isO
|
|||
if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "keycloak", Namespace: instance.Namespace}, keycloakDeployment); err != nil {
|
||||
logrus.Errorf("Deployment %s not found: %s", keycloakDeployment.Name, err)
|
||||
}
|
||||
deleteOpenShiftIdentityProviderProvisionCommand := identity_provider.GetDeleteOpenShiftIdentityProviderProvisionCommand(instance, isOpenShift4)
|
||||
podToExec, err := util.K8sclient.GetDeploymentPod(keycloakDeployment.Name, instance.Namespace)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to retrieve pod name. Further exec will fail")
|
||||
|
||||
providerName := "openshift-v3"
|
||||
if isOpenShift4 {
|
||||
providerName = "openshift-v4"
|
||||
}
|
||||
_, err = util.K8sclient.ExecIntoPod(podToExec, deleteOpenShiftIdentityProviderProvisionCommand, "delete OpenShift identity provider", instance.Namespace)
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
instance,
|
||||
keycloakDeployment.Name,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return identity_provider.GetIdentityProviderDeleteCommand(instance, providerName)
|
||||
},
|
||||
"delete OpenShift identity provider")
|
||||
if err == nil {
|
||||
oAuthClient := &oauth.OAuthClient{}
|
||||
oAuthClientName := instance.Spec.Auth.OAuthClientName
|
||||
|
|
|
|||
|
|
@ -109,9 +109,10 @@ const (
|
|||
CheEclipseOrg = "che.eclipse.org"
|
||||
|
||||
// che.eclipse.org annotations
|
||||
CheEclipseOrgMountPath = "che.eclipse.org/mount-path"
|
||||
CheEclipseOrgMountAs = "che.eclipse.org/mount-as"
|
||||
CheEclipseOrgEnvName = "che.eclipse.org/env-name"
|
||||
CheEclipseOrgMountPath = "che.eclipse.org/mount-path"
|
||||
CheEclipseOrgMountAs = "che.eclipse.org/mount-as"
|
||||
CheEclipseOrgEnvName = "che.eclipse.org/env-name"
|
||||
CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials"
|
||||
)
|
||||
|
||||
func InitDefaults(defaultsPath string) {
|
||||
|
|
|
|||
|
|
@ -46,37 +46,26 @@ var DeploymentDiffOpts = cmp.Options{
|
|||
}),
|
||||
}
|
||||
|
||||
type DeploymentProvisioningStatus struct {
|
||||
ProvisioningStatus
|
||||
Deployment *appsv1.Deployment
|
||||
}
|
||||
|
||||
func SyncDeploymentToCluster(
|
||||
deployContext *DeployContext,
|
||||
specDeployment *appsv1.Deployment,
|
||||
clusterDeployment *appsv1.Deployment,
|
||||
additionalDeploymentDiffOpts cmp.Options,
|
||||
additionalDeploymentMerge func(*appsv1.Deployment, *appsv1.Deployment) *appsv1.Deployment) DeploymentProvisioningStatus {
|
||||
additionalDeploymentMerge func(*appsv1.Deployment, *appsv1.Deployment) *appsv1.Deployment) (bool, error) {
|
||||
|
||||
if err := MountSecrets(specDeployment, deployContext); err != nil {
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Requeue: true, Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
clusterDeployment, err := GetClusterDeployment(specDeployment.Name, specDeployment.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
if clusterDeployment == nil {
|
||||
logrus.Infof("Creating a new object: %s, name %s", specDeployment.Kind, specDeployment.Name)
|
||||
err := deployContext.ClusterAPI.Client.Create(context.TODO(), specDeployment)
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Requeue: true, Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 2-step comparation process
|
||||
|
|
@ -89,9 +78,7 @@ func SyncDeploymentToCluster(
|
|||
fmt.Printf("Difference:\n%s", diff)
|
||||
clusterDeployment = additionalDeploymentMerge(specDeployment, clusterDeployment)
|
||||
err := deployContext.ClusterAPI.Client.Update(context.TODO(), clusterDeployment)
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Requeue: true, Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,19 +88,15 @@ func SyncDeploymentToCluster(
|
|||
fmt.Printf("Difference:\n%s", diff)
|
||||
clusterDeployment.Spec = specDeployment.Spec
|
||||
err := deployContext.ClusterAPI.Client.Update(context.TODO(), clusterDeployment)
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Requeue: true, Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
if clusterDeployment.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType && clusterDeployment.Status.Replicas > 1 {
|
||||
logrus.Infof("Deployment %s is in the rolling update state.", specDeployment.Name)
|
||||
}
|
||||
|
||||
return DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: ProvisioningStatus{Continue: clusterDeployment.Status.AvailableReplicas == 1 && clusterDeployment.Status.Replicas == 1},
|
||||
Deployment: clusterDeployment,
|
||||
}
|
||||
provisioned := clusterDeployment.Status.AvailableReplicas == 1 && clusterDeployment.Status.Replicas == 1
|
||||
return provisioned, nil
|
||||
}
|
||||
|
||||
func GetClusterDeployment(name string, namespace string, client runtimeClient.Client) (*appsv1.Deployment, error) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func SyncDevfileRegistryDeploymentToCluster(deployContext *deploy.DeployContext) deploy.DeploymentProvisioningStatus {
|
||||
func SyncDevfileRegistryDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
registryType := "devfile"
|
||||
registryImage := util.GetValue(deployContext.CheCluster.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(deployContext.CheCluster))
|
||||
registryImagePullPolicy := v1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Server.PluginRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
|
||||
|
|
@ -29,9 +29,7 @@ func SyncDevfileRegistryDeploymentToCluster(deployContext *deploy.DeployContext)
|
|||
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(deploy.DevfileRegistry, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
specDeployment, err := registry.GetSpecRegistryDeployment(
|
||||
|
|
@ -45,9 +43,7 @@ func SyncDevfileRegistryDeploymentToCluster(deployContext *deploy.DeployContext)
|
|||
probePath)
|
||||
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return deploy.SyncDeploymentToCluster(deployContext, specDeployment, clusterDeployment, nil, nil)
|
||||
|
|
|
|||
|
|
@ -76,15 +76,14 @@ func SyncDevfileRegistryToCluster(deployContext *deploy.DeployContext, cheHost s
|
|||
deployContext.InternalService.DevfileRegistryHost = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistry, deployContext.CheCluster.Namespace)
|
||||
|
||||
// Deploy devfile registry
|
||||
deploymentStatus := SyncDevfileRegistryDeploymentToCluster(deployContext)
|
||||
provisioned, err := SyncDevfileRegistryDeploymentToCluster(deployContext)
|
||||
if !util.IsTestMode() {
|
||||
if !deploymentStatus.Continue {
|
||||
if !provisioned {
|
||||
logrus.Info("Waiting on deployment '" + deploy.DevfileRegistry + "' to be ready")
|
||||
if deploymentStatus.Err != nil {
|
||||
logrus.Error(deploymentStatus.Err)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
return false, deploymentStatus.Err
|
||||
return provisioned, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
KeycloakDeploymentName = "keycloak"
|
||||
selectSslRequiredCommand = "OUT=$(psql keycloak -tAc \"SELECT 1 FROM REALM WHERE id = 'master'\"); " +
|
||||
"if [ $OUT -eq 1 ]; then psql keycloak -tAc \"SELECT ssl_required FROM REALM WHERE id = 'master'\"; fi"
|
||||
updateSslRequiredCommand = "psql keycloak -c \"update REALM set ssl_required='NONE' where id = 'master'\""
|
||||
|
|
@ -59,19 +58,15 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func SyncKeycloakDeploymentToCluster(deployContext *deploy.DeployContext) deploy.DeploymentProvisioningStatus {
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(KeycloakDeploymentName, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
func SyncKeycloakDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(IdentityProviderDeploymentName, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
specDeployment, err := getSpecKeycloakDeployment(deployContext, clusterDeployment)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return deploy.SyncDeploymentToCluster(deployContext, specDeployment, clusterDeployment, keycloakCustomDiffOpts, keycloakAdditionalDeploymentMerge)
|
||||
|
|
@ -81,7 +76,7 @@ func getSpecKeycloakDeployment(
|
|||
deployContext *deploy.DeployContext,
|
||||
clusterDeployment *appsv1.Deployment) (*appsv1.Deployment, error) {
|
||||
optionalEnv := true
|
||||
labels := deploy.GetLabels(deployContext.CheCluster, KeycloakDeploymentName)
|
||||
labels := deploy.GetLabels(deployContext.CheCluster, IdentityProviderDeploymentName)
|
||||
cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster)
|
||||
keycloakImage := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderImage, deploy.DefaultKeycloakImage(deployContext.CheCluster))
|
||||
pullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Auth.IdentityProviderImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(keycloakImage)))
|
||||
|
|
@ -553,7 +548,7 @@ func getSpecKeycloakDeployment(
|
|||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: KeycloakDeploymentName,
|
||||
Name: IdentityProviderDeploymentName,
|
||||
Namespace: deployContext.CheCluster.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: map[string]string{
|
||||
|
|
@ -577,7 +572,7 @@ func getSpecKeycloakDeployment(
|
|||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: KeycloakDeploymentName,
|
||||
Name: IdentityProviderDeploymentName,
|
||||
Image: keycloakImage,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Command: []string{
|
||||
|
|
@ -586,7 +581,7 @@ func getSpecKeycloakDeployment(
|
|||
Args: args,
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: KeycloakDeploymentName,
|
||||
Name: IdentityProviderDeploymentName,
|
||||
ContainerPort: 8080,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
|
|
@ -672,7 +667,7 @@ func isSslRequiredUpdatedForMasterRealm(deployContext *deploy.DeployContext) boo
|
|||
return false
|
||||
}
|
||||
|
||||
clusterDeployment, _ := deploy.GetClusterDeployment(KeycloakDeploymentName, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
clusterDeployment, _ := deploy.GetClusterDeployment(IdentityProviderDeploymentName, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if clusterDeployment == nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -686,23 +681,25 @@ func isSslRequiredUpdatedForMasterRealm(deployContext *deploy.DeployContext) boo
|
|||
return dbValue == "NONE"
|
||||
}
|
||||
|
||||
func getSslRequiredForMasterRealm(checluster *orgv1.CheCluster) (string, error) {
|
||||
podName, err := util.K8sclient.GetDeploymentPod(postgres.PostgresDeploymentName, checluster.Namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
stdout, err := util.K8sclient.ExecIntoPod(podName, selectSslRequiredCommand, "", checluster.Namespace)
|
||||
func getSslRequiredForMasterRealm(cr *orgv1.CheCluster) (string, error) {
|
||||
stdout, err := util.K8sclient.ExecIntoPod(
|
||||
cr,
|
||||
postgres.PostgresDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return selectSslRequiredCommand, nil
|
||||
},
|
||||
"")
|
||||
return strings.TrimSpace(stdout), err
|
||||
}
|
||||
|
||||
func updateSslRequiredForMasterRealm(checluster *orgv1.CheCluster) error {
|
||||
podName, err := util.K8sclient.GetDeploymentPod(postgres.PostgresDeploymentName, checluster.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = util.K8sclient.ExecIntoPod(podName, updateSslRequiredCommand, "Update ssl_required to NONE", checluster.Namespace)
|
||||
func updateSslRequiredForMasterRealm(cr *orgv1.CheCluster) error {
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
cr,
|
||||
postgres.PostgresDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return updateSslRequiredCommand, nil
|
||||
},
|
||||
"Update ssl_required to NONE")
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -721,13 +718,11 @@ func ProvisionKeycloakResources(deployContext *deploy.DeployContext) error {
|
|||
}
|
||||
}
|
||||
|
||||
keycloakProvisionCommand := GetKeycloakProvisionCommand(deployContext.CheCluster)
|
||||
podToExec, err := util.K8sclient.GetDeploymentPod(KeycloakDeploymentName, deployContext.CheCluster.Namespace)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to retrieve pod name. Further exec will fail")
|
||||
}
|
||||
|
||||
_, err = util.K8sclient.ExecIntoPod(podToExec, keycloakProvisionCommand, "create realm, client and user", deployContext.CheCluster.Namespace)
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
deployContext.CheCluster,
|
||||
IdentityProviderDeploymentName,
|
||||
GetKeycloakProvisionCommand,
|
||||
"create realm, client and user")
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ package identity_provider
|
|||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
v1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
|
|
@ -34,152 +33,140 @@ func GetPostgresProvisionCommand(identityProviderPostgresPassword string) (comma
|
|||
return command
|
||||
}
|
||||
|
||||
func GetKeycloakProvisionCommand(cr *v1.CheCluster) (command string) {
|
||||
requiredActions := ""
|
||||
updateAdminPassword := cr.Spec.Auth.UpdateAdminPassword
|
||||
func GetKeycloakProvisionCommand(cr *v1.CheCluster) (command string, err error) {
|
||||
cheFlavor := deploy.DefaultCheFlavor(cr)
|
||||
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
|
||||
keycloakClientId := util.GetValue(cr.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
|
||||
keycloakUserEnvVar := "${KEYCLOAK_USER}"
|
||||
keycloakPasswordEnvVar := "${KEYCLOAK_PASSWORD}"
|
||||
requiredActions := (map[bool]string{true: "\"UPDATE_PASSWORD\"", false: ""})[cr.Spec.Auth.UpdateAdminPassword]
|
||||
keycloakTheme := (map[bool]string{true: "rh-sso", false: "che"})[cheFlavor == "codeready"]
|
||||
realmDisplayName := (map[bool]string{true: "CodeReady Workspaces", false: "Eclipse Che"})[cheFlavor == "codeready"]
|
||||
|
||||
if updateAdminPassword {
|
||||
requiredActions = "\"UPDATE_PASSWORD\""
|
||||
script, keycloakRealm, keycloakClientId, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
|
||||
data := struct {
|
||||
Script string
|
||||
KeycloakAdminUserName string
|
||||
KeycloakAdminPassword string
|
||||
KeycloakRealm string
|
||||
RealmDisplayName string
|
||||
KeycloakTheme string
|
||||
CheHost string
|
||||
KeycloakClientId string
|
||||
RequiredActions string
|
||||
}{
|
||||
script,
|
||||
keycloakUserEnvVar,
|
||||
keycloakPasswordEnvVar,
|
||||
keycloakRealm,
|
||||
realmDisplayName,
|
||||
keycloakTheme,
|
||||
cr.Spec.Server.CheHost,
|
||||
keycloakClientId,
|
||||
requiredActions,
|
||||
}
|
||||
file, err := ioutil.ReadFile("/tmp/keycloak_provision")
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to locate keycloak entrypoint file: %s", err)
|
||||
}
|
||||
keycloakTheme := "che"
|
||||
realmDisplayName := "Eclipse Che"
|
||||
script := "/opt/jboss/keycloak/bin/kcadm.sh"
|
||||
if cheFlavor == "codeready" {
|
||||
keycloakTheme = "rh-sso"
|
||||
realmDisplayName = "CodeReady Workspaces"
|
||||
script = "/opt/eap/bin/kcadm.sh"
|
||||
keycloakUserEnvVar = "${SSO_ADMIN_USERNAME}"
|
||||
keycloakPasswordEnvVar = "${SSO_ADMIN_PASSWORD}"
|
||||
}
|
||||
str := string(file)
|
||||
r := strings.NewReplacer("$script", script,
|
||||
"$keycloakAdminUserName", keycloakUserEnvVar,
|
||||
"$keycloakAdminPassword", keycloakPasswordEnvVar,
|
||||
"$keycloakRealm", keycloakRealm,
|
||||
"$realmDisplayName", realmDisplayName,
|
||||
"$keycloakClientId", keycloakClientId,
|
||||
"$keycloakTheme", keycloakTheme,
|
||||
"$cheHost", cr.Spec.Server.CheHost,
|
||||
"$requiredActions", requiredActions)
|
||||
createRealmClientUserCommand := r.Replace(str)
|
||||
if cheFlavor == "che" {
|
||||
command = "cd /scripts && export JAVA_TOOL_OPTIONS=-Duser.home=. && " + createRealmClientUserCommand
|
||||
} else {
|
||||
command = "cd /home/jboss && " + createRealmClientUserCommand
|
||||
}
|
||||
return command
|
||||
return getCommandFromTemplateFile(cr, "/tmp/keycloak-provision.sh", data)
|
||||
}
|
||||
|
||||
func GetOpenShiftIdentityProviderProvisionCommand(cr *v1.CheCluster, oAuthClientName string, oauthSecret string, isOpenShift4 bool) (command string, err error) {
|
||||
cheFlavor := deploy.DefaultCheFlavor(cr)
|
||||
func GetOpenShiftIdentityProviderProvisionCommand(cr *v1.CheCluster, oAuthClientName string, oauthSecret string) (string, error) {
|
||||
isOpenShift4 := util.IsOpenShift4
|
||||
providerId := (map[bool]string{true: "openshift-v4", false: "openshift-v3"})[isOpenShift4]
|
||||
openShiftApiUrl, err := util.GetClusterPublicHostname(isOpenShift4)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to auto-detect public OpenShift API URL. Configure it in Identity provider details page in Keycloak admin console: %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
keycloakUserEnvVar := "${KEYCLOAK_USER}"
|
||||
keycloakPasswordEnvVar := "${KEYCLOAK_PASSWORD}"
|
||||
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
|
||||
script := "/opt/jboss/keycloak/bin/kcadm.sh"
|
||||
if cheFlavor == "codeready" {
|
||||
script = "/opt/eap/bin/kcadm.sh"
|
||||
keycloakUserEnvVar = "${SSO_ADMIN_USERNAME}"
|
||||
keycloakPasswordEnvVar = "${SSO_ADMIN_PASSWORD}"
|
||||
script, keycloakRealm, keycloakClientId, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
|
||||
data := struct {
|
||||
Script string
|
||||
KeycloakAdminUserName string
|
||||
KeycloakAdminPassword string
|
||||
KeycloakRealm string
|
||||
ProviderId string
|
||||
OAuthClientName string
|
||||
OauthSecret string
|
||||
OpenShiftApiUrl string
|
||||
KeycloakClientId string
|
||||
}{
|
||||
script,
|
||||
keycloakUserEnvVar,
|
||||
keycloakPasswordEnvVar,
|
||||
keycloakRealm,
|
||||
providerId,
|
||||
oAuthClientName,
|
||||
oauthSecret,
|
||||
openShiftApiUrl,
|
||||
keycloakClientId,
|
||||
}
|
||||
keycloakClientId := util.GetValue(cr.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
|
||||
return getCommandFromTemplateFile(cr, "/tmp/oauth-provision.sh", data)
|
||||
}
|
||||
|
||||
providerId := "openshift-v3"
|
||||
if isOpenShift4 {
|
||||
providerId = "openshift-v4"
|
||||
func GetGitHubIdentityProviderCreateCommand(deployContext *deploy.DeployContext) (string, error) {
|
||||
cr := deployContext.CheCluster
|
||||
script, keycloakRealm, _, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
|
||||
data := struct {
|
||||
Script string
|
||||
KeycloakAdminUserName string
|
||||
KeycloakAdminPassword string
|
||||
KeycloakRealm string
|
||||
ProviderId string
|
||||
}{
|
||||
script,
|
||||
keycloakUserEnvVar,
|
||||
keycloakPasswordEnvVar,
|
||||
keycloakRealm,
|
||||
"github",
|
||||
}
|
||||
return getCommandFromTemplateFile(cr, "/tmp/create-github-identity-provider.sh", data)
|
||||
}
|
||||
|
||||
file, err := ioutil.ReadFile("/tmp/oauth_provision")
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to locate keycloak oauth provisioning file: %s", err)
|
||||
func GetIdentityProviderDeleteCommand(cr *v1.CheCluster, identityProvider string) (string, error) {
|
||||
script, keycloakRealm, _, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
|
||||
data := struct {
|
||||
Script string
|
||||
KeycloakRealm string
|
||||
KeycloakAdminUserName string
|
||||
KeycloakAdminPassword string
|
||||
ProviderId string
|
||||
}{
|
||||
script,
|
||||
keycloakRealm,
|
||||
keycloakUserEnvVar,
|
||||
keycloakPasswordEnvVar,
|
||||
identityProvider,
|
||||
}
|
||||
createOpenShiftIdentityProviderTemplate := string(file)
|
||||
/*
|
||||
In order to have the token-exchange currently working and easily usable, we should (in case of Keycloak) be able to
|
||||
- Automatically redirect the user to its Keycloak account page to set those required values when the email is empty (instead of failing here: https://github.com/eclipse/che/blob/master/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakEnvironmentInitalizationFilter.java#L125)
|
||||
- Or at least point with a link to the place where it can be set (the KeycloakSettings PROFILE_ENDPOINT_SETTING value)
|
||||
(cf. here: https://github.com/eclipse/che/blob/master/multiuser/keycloak/che-multiuser-keycloak-server/src/main/java/org/eclipse/che/multiuser/keycloak/server/KeycloakSettings.java#L117)
|
||||
*/
|
||||
return getCommandFromTemplateFile(cr, "/tmp/delete-identity-provider.sh", data)
|
||||
}
|
||||
|
||||
template, err := template.New("IdentityProviderProvisioning").Parse(createOpenShiftIdentityProviderTemplate)
|
||||
func getCommandFromTemplateFile(cr *v1.CheCluster, templateFile string, data interface{}) (string, error) {
|
||||
cheFlavor := deploy.DefaultCheFlavor(cr)
|
||||
|
||||
file, err := ioutil.ReadFile(templateFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
template, err := template.New("Template").Parse(string(file))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
err = template.Execute(
|
||||
buffer,
|
||||
struct {
|
||||
Script string
|
||||
KeycloakAdminUserName string
|
||||
KeycloakAdminPassword string
|
||||
KeycloakRealm string
|
||||
ProviderId string
|
||||
OAuthClientName string
|
||||
OauthSecret string
|
||||
OpenShiftApiUrl string
|
||||
KeycloakClientId string
|
||||
}{
|
||||
script,
|
||||
keycloakUserEnvVar,
|
||||
keycloakPasswordEnvVar,
|
||||
keycloakRealm,
|
||||
providerId,
|
||||
oAuthClientName,
|
||||
oauthSecret,
|
||||
openShiftApiUrl,
|
||||
keycloakClientId,
|
||||
})
|
||||
err = template.Execute(buffer, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if cheFlavor == "che" {
|
||||
command = "cd /scripts && export JAVA_TOOL_OPTIONS=-Duser.home=. && " + buffer.String()
|
||||
} else {
|
||||
command = "cd /home/jboss && " + buffer.String()
|
||||
return "cd /scripts && export JAVA_TOOL_OPTIONS=-Duser.home=. && " + buffer.String(), nil
|
||||
}
|
||||
return command, nil
|
||||
return "cd /home/jboss && " + buffer.String(), nil
|
||||
}
|
||||
|
||||
func GetDeleteOpenShiftIdentityProviderProvisionCommand(cr *v1.CheCluster, isOpenShift4 bool) (command string) {
|
||||
func getDefaults(cr *v1.CheCluster) (string, string, string, string, string) {
|
||||
cheFlavor := deploy.DefaultCheFlavor(cr)
|
||||
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
|
||||
script := "/opt/jboss/keycloak/bin/kcadm.sh"
|
||||
keycloakUserEnvVar := "${KEYCLOAK_USER}"
|
||||
keycloakPasswordEnvVar := "${KEYCLOAK_PASSWORD}"
|
||||
keycloakClientId := util.GetValue(cr.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
|
||||
if cheFlavor == "codeready" {
|
||||
script = "/opt/eap/bin/kcadm.sh"
|
||||
keycloakUserEnvVar = "${SSO_ADMIN_USERNAME}"
|
||||
keycloakPasswordEnvVar = "${SSO_ADMIN_PASSWORD}"
|
||||
return "/opt/eap/bin/kcadm.sh", keycloakRealm, keycloakClientId, "${SSO_ADMIN_USERNAME}", "${SSO_ADMIN_PASSWORD}"
|
||||
}
|
||||
|
||||
providerName := "openshift-v3"
|
||||
if isOpenShift4 {
|
||||
providerName = "openshift-v4"
|
||||
}
|
||||
deleteOpenShiftIdentityProviderCommand :=
|
||||
script + " config credentials --server http://0.0.0.0:8080/auth " +
|
||||
"--realm master --user " + keycloakUserEnvVar + " --password " + keycloakPasswordEnvVar + " && " +
|
||||
"if " + script + " get identity-provider/instances/" + providerName + " -r " + keycloakRealm + " ; then " +
|
||||
script + " delete identity-provider/instances/" + providerName + " -r " + keycloakRealm + " ; fi"
|
||||
if cheFlavor == "che" {
|
||||
command = "cd /scripts && export JAVA_TOOL_OPTIONS=-Duser.home=. && " + deleteOpenShiftIdentityProviderCommand
|
||||
} else {
|
||||
command = "cd /home/jboss && " + deleteOpenShiftIdentityProviderCommand
|
||||
}
|
||||
return command
|
||||
return "/opt/jboss/keycloak/bin/kcadm.sh", keycloakRealm, keycloakClientId, "${KEYCLOAK_USER}", "${KEYCLOAK_PASSWORD}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,106 +16,128 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/deploy"
|
||||
"github.com/eclipse/che-operator/pkg/deploy/expose"
|
||||
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
oauth "github.com/openshift/api/oauth/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const (
|
||||
Keycloak = "keycloak"
|
||||
IdentityProviderServiceName = "keycloak"
|
||||
IdentityProviderExposureName = "keycloak"
|
||||
IdentityProviderDeploymentName = "keycloak"
|
||||
)
|
||||
|
||||
var (
|
||||
oAuthClientDiffOpts = cmpopts.IgnoreFields(oauth.OAuthClient{}, "TypeMeta", "ObjectMeta")
|
||||
syncItems = []func(*deploy.DeployContext) (bool, error){
|
||||
syncService,
|
||||
syncExposure,
|
||||
SyncKeycloakDeploymentToCluster,
|
||||
syncKeycloakResources,
|
||||
syncOpenShiftIdentityProvider,
|
||||
SyncGitHubOAuth,
|
||||
}
|
||||
)
|
||||
|
||||
// SyncIdentityProviderToCluster instantiates the identity provider (Keycloak) in the cluster. Returns true if
|
||||
// the provisioning is complete, false if requeue of the reconcile request is needed.
|
||||
func SyncIdentityProviderToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
instance := deployContext.CheCluster
|
||||
cheHost := instance.Spec.Server.CheHost
|
||||
protocol := "http"
|
||||
if instance.Spec.Server.TlsSupport {
|
||||
protocol = "https"
|
||||
}
|
||||
cheFlavor := deploy.DefaultCheFlavor(instance)
|
||||
cheMultiUser := deploy.GetCheMultiUser(instance)
|
||||
tests := util.IsTestMode()
|
||||
isOpenShift := util.IsOpenShift
|
||||
|
||||
if instance.Spec.Auth.ExternalIdentityProvider {
|
||||
cr := deployContext.CheCluster
|
||||
if cr.Spec.Auth.ExternalIdentityProvider {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
cheMultiUser := deploy.GetCheMultiUser(cr)
|
||||
if cheMultiUser == "false" {
|
||||
if util.K8sclient.IsDeploymentExists("keycloak", instance.Namespace) {
|
||||
util.K8sclient.DeleteDeployment("keycloak", instance.Namespace)
|
||||
if util.K8sclient.IsDeploymentExists("keycloak", cr.Namespace) {
|
||||
util.K8sclient.DeleteDeployment("keycloak", cr.Namespace)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
keycloakLabels := deploy.GetLabels(instance, "keycloak")
|
||||
|
||||
serviceStatus := deploy.SyncServiceToCluster(deployContext, "keycloak", []string{"http"}, []int32{8080}, keycloakLabels)
|
||||
if !tests {
|
||||
if !serviceStatus.Continue {
|
||||
logrus.Info("Waiting on service 'keycloak' to be ready")
|
||||
if serviceStatus.Err != nil {
|
||||
logrus.Error(serviceStatus.Err)
|
||||
for _, syncItem := range syncItems {
|
||||
provisioned, err := syncItem(deployContext)
|
||||
if !util.IsTestMode() {
|
||||
if !provisioned {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, serviceStatus.Err
|
||||
}
|
||||
}
|
||||
|
||||
additionalLabels := (map[bool]string{true: instance.Spec.Auth.IdentityProviderRoute.Labels, false: instance.Spec.Auth.IdentityProviderIngress.Labels})[util.IsOpenShift]
|
||||
endpoint, done, err := expose.Expose(deployContext, cheHost, Keycloak, additionalLabels)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func syncService(deployContext *deploy.DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
labels := deploy.GetLabels(cr, IdentityProviderServiceName)
|
||||
|
||||
serviceStatus := deploy.SyncServiceToCluster(
|
||||
deployContext,
|
||||
IdentityProviderServiceName,
|
||||
[]string{"http"},
|
||||
[]int32{8080},
|
||||
labels)
|
||||
|
||||
return serviceStatus.Continue, serviceStatus.Err
|
||||
}
|
||||
|
||||
func syncExposure(deployContext *deploy.DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
|
||||
protocol := (map[bool]string{
|
||||
true: "https",
|
||||
false: "http"})[cr.Spec.Server.TlsSupport]
|
||||
additionalLabels := (map[bool]string{
|
||||
true: cr.Spec.Auth.IdentityProviderRoute.Labels,
|
||||
false: cr.Spec.Auth.IdentityProviderIngress.Labels})[util.IsOpenShift]
|
||||
|
||||
endpoint, done, err := expose.Expose(
|
||||
deployContext,
|
||||
cr.Spec.Server.CheHost,
|
||||
IdentityProviderExposureName,
|
||||
additionalLabels)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
keycloakURL := protocol + "://" + endpoint
|
||||
deployContext.InternalService.KeycloakHost = fmt.Sprintf("%s://%s.%s.svc:%d", "http", "keycloak", deployContext.CheCluster.Namespace, 8080)
|
||||
|
||||
if instance.Spec.Auth.IdentityProviderURL != keycloakURL {
|
||||
instance.Spec.Auth.IdentityProviderURL = keycloakURL
|
||||
keycloakURL := protocol + "://" + endpoint
|
||||
deployContext.InternalService.KeycloakHost = fmt.Sprintf("%s://%s.%s.svc:%d", "http", "keycloak", cr.Namespace, 8080)
|
||||
|
||||
if cr.Spec.Auth.IdentityProviderURL != keycloakURL {
|
||||
cr.Spec.Auth.IdentityProviderURL = keycloakURL
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak URL", keycloakURL); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
instance.Status.KeycloakURL = keycloakURL
|
||||
cr.Status.KeycloakURL = keycloakURL
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "Keycloak URL", keycloakURL); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
deploymentStatus := SyncKeycloakDeploymentToCluster(deployContext)
|
||||
if !tests {
|
||||
if !deploymentStatus.Continue {
|
||||
logrus.Info("Waiting on deployment 'keycloak' to be ready")
|
||||
if deploymentStatus.Err != nil {
|
||||
logrus.Error(deploymentStatus.Err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, deploymentStatus.Err
|
||||
}
|
||||
}
|
||||
|
||||
if !tests {
|
||||
if !instance.Status.KeycloakProvisoned {
|
||||
func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) {
|
||||
if !util.IsTestMode() {
|
||||
cr := deployContext.CheCluster
|
||||
if !cr.Status.KeycloakProvisoned {
|
||||
if err := ProvisionKeycloakResources(deployContext); err != nil {
|
||||
logrus.Error(err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for {
|
||||
instance.Status.KeycloakProvisoned = true
|
||||
cr.Status.KeycloakProvisoned = true
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil &&
|
||||
errors.IsConflict(err) {
|
||||
|
||||
|
|
@ -127,61 +149,64 @@ func SyncIdentityProviderToCluster(deployContext *deploy.DeployContext) (bool, e
|
|||
}
|
||||
}
|
||||
|
||||
if isOpenShift && util.IsOAuthEnabled(instance) {
|
||||
if err := SyncIdentityProviderItems(deployContext, cheFlavor); err != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func syncOpenShiftIdentityProvider(deployContext *deploy.DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
if util.IsOpenShift && util.IsOAuthEnabled(cr) {
|
||||
return SyncOpenShiftIdentityProviderItems(deployContext)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
|
||||
oAuthClientName := cr.Spec.Auth.OAuthClientName
|
||||
if len(oAuthClientName) < 1 {
|
||||
oAuthClientName = cr.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
|
||||
cr.Spec.Auth.OAuthClientName = oAuthClientName
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SyncIdentityProviderItems(deployContext *deploy.DeployContext, cheFlavor string) error {
|
||||
instance := deployContext.CheCluster
|
||||
tests := util.IsTestMode()
|
||||
isOpenShift4 := util.IsOpenShift4
|
||||
keycloakDeploymentName := KeycloakDeploymentName
|
||||
oAuthClientName := instance.Spec.Auth.OAuthClientName
|
||||
if len(oAuthClientName) < 1 {
|
||||
oAuthClientName = instance.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
|
||||
instance.Spec.Auth.OAuthClientName = oAuthClientName
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
oauthSecret := instance.Spec.Auth.OAuthSecret
|
||||
oauthSecret := cr.Spec.Auth.OAuthSecret
|
||||
if len(oauthSecret) < 1 {
|
||||
oauthSecret = util.GeneratePasswd(12)
|
||||
instance.Spec.Auth.OAuthSecret = oauthSecret
|
||||
cr.Spec.Auth.OAuthSecret = oauthSecret
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "oAuth secret name", oauthSecret); err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
keycloakURL := instance.Spec.Auth.IdentityProviderURL
|
||||
keycloakRealm := util.GetValue(instance.Spec.Auth.IdentityProviderRealm, cheFlavor)
|
||||
oAuthClient := deploy.NewOAuthClient(oAuthClientName, oauthSecret, keycloakURL, keycloakRealm, isOpenShift4)
|
||||
if _, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts); err != nil {
|
||||
return err
|
||||
keycloakURL := cr.Spec.Auth.IdentityProviderURL
|
||||
cheFlavor := deploy.DefaultCheFlavor(cr)
|
||||
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
|
||||
oAuthClient := deploy.NewOAuthClient(oAuthClientName, oauthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
|
||||
provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
|
||||
if !provisioned {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !tests && !instance.Status.OpenShiftoAuthProvisioned {
|
||||
// note that this uses the instance.Spec.Auth.IdentityProviderRealm and instance.Spec.Auth.IdentityProviderClientId.
|
||||
// because we're not doing much of a change detection on those fields, we can't react on them changing here.
|
||||
openShiftIdentityProviderCommand, err := GetOpenShiftIdentityProviderProvisionCommand(instance, oAuthClientName, oauthSecret, isOpenShift4)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to build identity provider provisioning command")
|
||||
return err
|
||||
}
|
||||
podToExec, err := util.K8sclient.GetDeploymentPod(keycloakDeploymentName, instance.Namespace)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to retrieve pod name. Further exec will fail")
|
||||
return err
|
||||
}
|
||||
_, err = util.K8sclient.ExecIntoPod(podToExec, openShiftIdentityProviderCommand, "create OpenShift identity provider", instance.Namespace)
|
||||
if err == nil {
|
||||
if !util.IsTestMode() {
|
||||
if !cr.Status.OpenShiftoAuthProvisioned {
|
||||
// note that this uses the instance.Spec.Auth.IdentityProviderRealm and instance.Spec.Auth.IdentityProviderClientId.
|
||||
// because we're not doing much of a change detection on those fields, we can't react on them changing here.
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
cr,
|
||||
IdentityProviderDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return GetOpenShiftIdentityProviderProvisionCommand(cr, oAuthClientName, oauthSecret)
|
||||
},
|
||||
"Create OpenShift identity provider")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for {
|
||||
instance.Status.OpenShiftoAuthProvisioned = true
|
||||
cr.Status.OpenShiftoAuthProvisioned = true
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil &&
|
||||
errors.IsConflict(err) {
|
||||
|
||||
|
|
@ -192,7 +217,80 @@ func SyncIdentityProviderItems(deployContext *deploy.DeployContext, cheFlavor st
|
|||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SyncGitHubOAuth provisions GitHub OAuth if secret with
|
||||
// annotation `che.eclipse.org/github-oauth-credentials=true` is mounted into a container
|
||||
func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
|
||||
// find mounted GitHug OAuth credentials
|
||||
secrets := &corev1.SecretList{}
|
||||
|
||||
kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.Equals, []string{deploy.CheEclipseOrg})
|
||||
kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesComponentLabelKey, selection.Equals, []string{IdentityProviderDeploymentName + "-secret"})
|
||||
|
||||
listOptions := &client.ListOptions{
|
||||
LabelSelector: labels.NewSelector().
|
||||
Add(*kubernetesPartOfLabelSelectorRequirement).
|
||||
Add(*kubernetesComponentLabelSelectorRequirement),
|
||||
}
|
||||
if err := deployContext.ClusterAPI.Client.List(context.TODO(), secrets, listOptions); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
isGitHubOAuthCredentialsExists := false
|
||||
for _, secret := range secrets.Items {
|
||||
if secret.Annotations[deploy.CheEclipseOrgGithubOAuthCredentials] == "true" {
|
||||
isGitHubOAuthCredentialsExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isGitHubOAuthCredentialsExists {
|
||||
if !cr.Status.GitHubOAuthProvisioned {
|
||||
if !util.IsTestMode() {
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
cr,
|
||||
IdentityProviderDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return GetGitHubIdentityProviderCreateCommand(deployContext)
|
||||
},
|
||||
"Create GitHub OAuth")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
cr.Status.GitHubOAuthProvisioned = true
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "true"); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cr.Status.GitHubOAuthProvisioned {
|
||||
if !util.IsTestMode() {
|
||||
_, err := util.K8sclient.ExecIntoPod(
|
||||
cr,
|
||||
IdentityProviderDeploymentName,
|
||||
func(cr *orgv1.CheCluster) (string, error) {
|
||||
return GetIdentityProviderDeleteCommand(cr, "github")
|
||||
},
|
||||
"Delete GitHub OAuth")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
cr.Status.GitHubOAuthProvisioned = false
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "false"); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func reload(deployContext *deploy.DeployContext) error {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// Copyright (c) 2012-2019 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
package identity_provider
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/eclipse/che-operator/pkg/deploy"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSyncGitHubOAuth(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
initCR *orgv1.CheCluster
|
||||
expectedCR *orgv1.CheCluster
|
||||
initObjects []runtime.Object
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "Should provision GitHub OAuth",
|
||||
initCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
},
|
||||
expectedCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Status: orgv1.CheClusterStatus{
|
||||
GitHubOAuthProvisioned: true,
|
||||
},
|
||||
},
|
||||
initObjects: []runtime.Object{
|
||||
&corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "github-credentials",
|
||||
Namespace: "eclipse-che",
|
||||
Labels: map[string]string{
|
||||
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
|
||||
deploy.KubernetesComponentLabelKey: "keycloak-secret",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
deploy.CheEclipseOrgGithubOAuthCredentials: "true",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key": []byte("key-data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should not provision GitHub OAuth",
|
||||
initCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
},
|
||||
expectedCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
},
|
||||
initObjects: []runtime.Object{
|
||||
&corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "github-credentials",
|
||||
Namespace: "eclipse-che",
|
||||
Labels: map[string]string{
|
||||
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
|
||||
deploy.KubernetesComponentLabelKey: "keycloak-secret",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key": []byte("key-data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should delete GitHub OAuth",
|
||||
initCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Status: orgv1.CheClusterStatus{
|
||||
GitHubOAuthProvisioned: true,
|
||||
},
|
||||
},
|
||||
expectedCR: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Status: orgv1.CheClusterStatus{
|
||||
GitHubOAuthProvisioned: false,
|
||||
},
|
||||
},
|
||||
initObjects: []runtime.Object{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
testCase.initObjects = append(testCase.initObjects, testCase.initCR)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
|
||||
|
||||
deployContext := &deploy.DeployContext{
|
||||
CheCluster: testCase.initCR,
|
||||
ClusterAPI: deploy.ClusterAPI{
|
||||
Client: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := SyncGitHubOAuth(deployContext)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mounting secret: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedCR, testCase.initCR) {
|
||||
t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedCR, testCase.initCR))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func SyncPluginRegistryDeploymentToCluster(deployContext *deploy.DeployContext) deploy.DeploymentProvisioningStatus {
|
||||
func SyncPluginRegistryDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
registryType := "plugin"
|
||||
registryImage := util.GetValue(deployContext.CheCluster.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(deployContext.CheCluster))
|
||||
registryImagePullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Server.PluginRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
|
||||
|
|
@ -29,9 +29,7 @@ func SyncPluginRegistryDeploymentToCluster(deployContext *deploy.DeployContext)
|
|||
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(deploy.PluginRegistry, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
specDeployment, err := registry.GetSpecRegistryDeployment(
|
||||
|
|
@ -45,9 +43,7 @@ func SyncPluginRegistryDeploymentToCluster(deployContext *deploy.DeployContext)
|
|||
probePath)
|
||||
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return deploy.SyncDeploymentToCluster(deployContext, specDeployment, clusterDeployment, nil, nil)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ package plugin_registry
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/eclipse/che-operator/pkg/deploy"
|
||||
"github.com/eclipse/che-operator/pkg/deploy/expose"
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
|
|
@ -77,15 +78,14 @@ func SyncPluginRegistryToCluster(deployContext *deploy.DeployContext, cheHost st
|
|||
deployContext.InternalService.PluginRegistryHost = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistry, deployContext.CheCluster.Namespace)
|
||||
|
||||
// Deploy plugin registry
|
||||
deploymentStatus := SyncPluginRegistryDeploymentToCluster(deployContext)
|
||||
provisioned, err := SyncPluginRegistryDeploymentToCluster(deployContext)
|
||||
if !util.IsTestMode() {
|
||||
if !deploymentStatus.Continue {
|
||||
if !provisioned {
|
||||
logrus.Info("Waiting on deployment '" + deploy.PluginRegistry + "' to be ready")
|
||||
if deploymentStatus.Err != nil {
|
||||
logrus.Error(deploymentStatus.Err)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
return false, deploymentStatus.Err
|
||||
return provisioned, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,19 +30,15 @@ var (
|
|||
postgresAdminPassword = util.GeneratePasswd(12)
|
||||
)
|
||||
|
||||
func SyncPostgresDeploymentToCluster(deployContext *deploy.DeployContext) deploy.DeploymentProvisioningStatus {
|
||||
func SyncPostgresDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(PostgresDeploymentName, deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
specDeployment, err := getSpecPostgresDeployment(deployContext, clusterDeployment)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return deploy.SyncDeploymentToCluster(deployContext, specDeployment, clusterDeployment, nil, nil)
|
||||
|
|
|
|||
|
|
@ -27,19 +27,15 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func SyncCheDeploymentToCluster(deployContext *deploy.DeployContext) deploy.DeploymentProvisioningStatus {
|
||||
func SyncCheDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
clusterDeployment, err := deploy.GetClusterDeployment(deploy.DefaultCheFlavor(deployContext.CheCluster), deployContext.CheCluster.Namespace, deployContext.ClusterAPI.Client)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
specDeployment, err := getSpecCheDeployment(deployContext)
|
||||
if err != nil {
|
||||
return deploy.DeploymentProvisioningStatus{
|
||||
ProvisioningStatus: deploy.ProvisioningStatus{Err: err},
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return deploy.SyncDeploymentToCluster(deployContext, specDeployment, clusterDeployment, nil, nil)
|
||||
|
|
|
|||
|
|
@ -100,9 +100,7 @@ func update(deployContext *DeployContext, actual runtime.Object, blueprint metav
|
|||
}
|
||||
|
||||
err = clusterAPI.Client.Create(context.TODO(), obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, err
|
||||
} else {
|
||||
obj, err := setOwnerReferenceAndConvertToRuntime(deployContext, blueprint)
|
||||
if err != nil {
|
||||
|
|
@ -113,14 +111,10 @@ func update(deployContext *DeployContext, actual runtime.Object, blueprint metav
|
|||
obj.(metav1.Object).SetResourceVersion(actualMeta.GetResourceVersion())
|
||||
|
||||
err = clusterAPI.Client.Update(context.TODO(), obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func isUpdateUsingDeleteCreate(kind string) bool {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
v1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -53,7 +54,26 @@ func GetK8Client() *k8s {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cl *k8s) ExecIntoPod(podName string, command string, reason string, namespace string) (string, error) {
|
||||
func (cl *k8s) ExecIntoPod(
|
||||
cr *v1.CheCluster,
|
||||
deploymentName string,
|
||||
getCommand func(*v1.CheCluster) (string, error),
|
||||
reason string) (string, error) {
|
||||
|
||||
command, err := getCommand(cr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pod, err := cl.GetDeploymentPod(deploymentName, cr.Namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cl.DoExecIntoPod(cr.Namespace, pod, command, reason)
|
||||
}
|
||||
|
||||
func (cl *k8s) DoExecIntoPod(namespace string, podName string, command string, reason string) (string, error) {
|
||||
if reason != "" {
|
||||
logrus.Infof("Running exec for '%s' in the pod '%s'", reason, podName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
# This program and the accompanying materials are made
|
||||
# available under the terms of the Eclipse Public License 2.0
|
||||
# which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Red Hat, Inc. - initial API and implementation
|
||||
#
|
||||
|
||||
connectToKeycloak() {
|
||||
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
|
||||
}
|
||||
|
||||
createIdentityProvider() {
|
||||
{{ .Script }} get identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "{{ .ProviderId }} identity provider exists."
|
||||
else
|
||||
echo "Create new {{ .ProviderId }} identity provider."
|
||||
if [ -z "${GITHUB_CLIENT_ID}" ] || [ -z "${GITHUB_SECRET}" ]; then
|
||||
echo "Either 'GITHUB_CLIENT_ID' or 'GITHUB_SECRET' aren't set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
{{ .Script }} create identity-provider/instances \
|
||||
-r {{ .KeycloakRealm }} \
|
||||
-s alias={{ .ProviderId }} \
|
||||
-s providerId={{ .ProviderId }} \
|
||||
-s enabled=true \
|
||||
-s storeToken=true \
|
||||
-s config.useJwksUrl=true \
|
||||
-s config.clientId=${GITHUB_CLIENT_ID} \
|
||||
-s config.clientSecret=${GITHUB_SECRET} \
|
||||
-s config.defaultScope=repo,user,write:public_key
|
||||
fi
|
||||
}
|
||||
|
||||
connectToKeycloak
|
||||
createIdentityProvider
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
# This program and the accompanying materials are made
|
||||
# available under the terms of the Eclipse Public License 2.0
|
||||
# which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Red Hat, Inc. - initial API and implementation
|
||||
#
|
||||
|
||||
connectToKeycloak() {
|
||||
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
|
||||
}
|
||||
|
||||
deleteIdentityProvider() {
|
||||
{{ .Script }} get identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "{{ .ProviderId }} identity provider does not exists."
|
||||
else
|
||||
echo "Delete {{ .ProviderId }} identity provider."
|
||||
{{ .Script }} delete identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
|
||||
fi
|
||||
}
|
||||
|
||||
connectToKeycloak
|
||||
deleteIdentityProvider
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
# This program and the accompanying materials are made
|
||||
# available under the terms of the Eclipse Public License 2.0
|
||||
# which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Red Hat, Inc. - initial API and implementation
|
||||
#
|
||||
|
||||
connectToKeycloak() {
|
||||
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
|
||||
}
|
||||
|
||||
provisionKeycloak() {
|
||||
{{ .Script }} update realms/master -s sslRequired=none
|
||||
{{ .Script }} config truststore --trustpass ${SSO_TRUSTSTORE_PASSWORD} ${SSO_TRUSTSTORE_DIR}/${SSO_TRUSTSTORE}
|
||||
|
||||
{{ .Script }} get realms/{{ .KeycloakRealm }}
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "{{ .KeycloakRealm }} realm exists."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Provision {{ .KeycloakRealm }} realm."
|
||||
{{ .Script }} create realms \
|
||||
-s realm='{{ .KeycloakRealm }}' \
|
||||
-s displayName='{{ .RealmDisplayName }}' \
|
||||
-s enabled=true \
|
||||
-s sslRequired=none \
|
||||
-s registrationAllowed=false \
|
||||
-s resetPasswordAllowed=true \
|
||||
-s loginTheme={{ .KeycloakTheme }} \
|
||||
-s accountTheme={{ .KeycloakTheme }} \
|
||||
-s adminTheme={{ .KeycloakTheme }} \
|
||||
-s emailTheme={{ .KeycloakTheme }}
|
||||
|
||||
{{ .Script }} create clients \
|
||||
-r '{{ .KeycloakRealm }}' \
|
||||
-s clientId={{ .KeycloakClientId }} \
|
||||
-s id={{ .KeycloakClientId }} \
|
||||
-s webOrigins='["http://{{ .CheHost }}", "https://{{ .CheHost }}"]' \
|
||||
-s redirectUris='["http://{{ .CheHost }}/dashboard/*", "https://{{ .CheHost }}/dashboard/*", "http://{{ .CheHost }}/workspace-loader/*", "https://{{ .CheHost }}/workspace-loader/*", "http://{{ .CheHost }}/_app/*", "https://{{ .CheHost }}/_app/*", "http://{{ .CheHost }}/swagger/*", "https://{{ .CheHost }}/swagger/*"]' \
|
||||
-s directAccessGrantsEnabled=true \
|
||||
-s publicClient=true
|
||||
|
||||
{{ .Script }} create users \
|
||||
-r '{{ .KeycloakRealm }}' \
|
||||
-s username=admin \
|
||||
-s email=\"admin@admin.com\" \
|
||||
-s enabled=true \
|
||||
-s requiredActions='[{{ .RequiredActions }}]'
|
||||
|
||||
{{ .Script }} set-password \
|
||||
-r '{{ .KeycloakRealm }}' \
|
||||
--username admin \
|
||||
--new-password admin
|
||||
|
||||
{{ .Script }} add-roles \
|
||||
-r '{{ .KeycloakRealm }}' \
|
||||
--uusername admin \
|
||||
--cclientid broker \
|
||||
--rolename read-token
|
||||
|
||||
CLIENT_ID=$({{ .Script }} get clients -r '{{ .KeycloakRealm }}' -q clientId=broker | sed -n 's/.*"id" *: *"\([^"]\+\).*/\1/p')
|
||||
{{ .Script }} update clients/${CLIENT_ID} \
|
||||
-r '{{ .KeycloakRealm }}' \
|
||||
-s "defaultRoles+=read-token"
|
||||
}
|
||||
|
||||
connectToKeycloak
|
||||
provisionKeycloak
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
$script config credentials --server http://0.0.0.0:8080/auth \
|
||||
--realm master \
|
||||
--user $keycloakAdminUserName \
|
||||
--password $keycloakAdminPassword \
|
||||
&& $script update realms/master -s sslRequired=none \
|
||||
&& $script config truststore --trustpass ${SSO_TRUSTSTORE_PASSWORD} ${SSO_TRUSTSTORE_DIR}/${SSO_TRUSTSTORE} \
|
||||
&& $script get realms/$keycloakRealm; \
|
||||
if [ $? -eq 0 ]; then echo "Realm exists"; exit 0; fi \
|
||||
&& $script create realms -s realm='$keycloakRealm' \
|
||||
-s displayName='$realmDisplayName' \
|
||||
-s enabled=true \
|
||||
-s sslRequired=none \
|
||||
-s registrationAllowed=false \
|
||||
-s resetPasswordAllowed=true \
|
||||
-s loginTheme=$keycloakTheme \
|
||||
-s accountTheme=$keycloakTheme \
|
||||
-s adminTheme=$keycloakTheme \
|
||||
-s emailTheme=$keycloakTheme \
|
||||
&& $script create clients -r '$keycloakRealm' \
|
||||
-s clientId=$keycloakClientId \
|
||||
-s id=$keycloakClientId \
|
||||
-s 'webOrigins=["http://$cheHost", "https://$cheHost"]' \
|
||||
-s 'redirectUris=["http://$cheHost/dashboard/*", "https://$cheHost/dashboard/*", "http://$cheHost/workspace-loader/*", "https://$cheHost/workspace-loader/*", "http://$cheHost/_app/*", "https://$cheHost/_app/*", "http://$cheHost/swagger/*", "https://$cheHost/swagger/*"]' \
|
||||
-s 'directAccessGrantsEnabled'=true \
|
||||
-s publicClient=true \
|
||||
&& $script create users -s username=admin \
|
||||
-s email=\"admin@admin.com\" \
|
||||
-s enabled=true -r '$keycloakRealm' \
|
||||
-s 'requiredActions=[$requiredActions]' \
|
||||
&& $script set-password -r '$keycloakRealm' --username admin \
|
||||
--new-password admin \
|
||||
&& $script add-roles -r '$keycloakRealm' \
|
||||
--uusername admin \
|
||||
--cclientid broker \
|
||||
--rolename read-token \
|
||||
&& CLIENT_ID=$($script get clients -r '$keycloakRealm' -q clientId=broker | sed -n 's/.*"id" *: *"\([^"]\+\).*/\1/p') \
|
||||
&& $script update clients/$CLIENT_ID -r '$keycloakRealm' -s "defaultRoles+=read-token"
|
||||
|
|
@ -1,3 +1,15 @@
|
|||
#
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
# This program and the accompanying materials are made
|
||||
# available under the terms of the Eclipse Public License 2.0
|
||||
# which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Red Hat, Inc. - initial API and implementation
|
||||
#
|
||||
|
||||
connect_to_keycloak() {
|
||||
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
|
||||
{{ .Script }} config truststore --trustpass ${SSO_TRUSTSTORE_PASSWORD} ${SSO_TRUSTSTORE_DIR}/${SSO_TRUSTSTORE}
|
||||
Loading…
Reference in New Issue