THIS IS ONLY A TEST INSTANCE. DON'T DO IMPORTANT WORK HERE!

Unverified Commit 2426068f authored by Eugene Ivantsov's avatar Eugene Ivantsov Committed by GitHub
Browse files

Rename json tags. Update upstream image tag. Remove CRW olm yamls (#13)

* Renamed json tags. Updated upstream image tag. Removed CRW olm stuff
parent c34afd35
......@@ -20,8 +20,10 @@ The project is in its early development and breaking changes are possible.
## How to Deploy
**IMPORTANT! Cluster Admin privileges are required**
```
./deploy.sh
./deploy.sh $namespace
```
The script will create sa, role, role binding, operator deployment, CRD and CR.
......@@ -38,15 +40,17 @@ When on pure k8s, make sure you provide a global ingress domain in `deploy/crds/
### OpenShift oAuth
Bear in mind that che-operator service account needs to have cluster admin privileges so that the operator can create oauthclient at a cluster scope.
There is `oc adm` command in both scripts. Uncomment it if you need these features.
There is `oc adm` command in both deploy scripts. Uncomment it if you need this feature.
Make sure your current user has cluster-admin privileges.
### TLS
#### OpenShift
When using self-signed certificates make sure you set `server.selfSignedCerts` to true and grant che-operator service account cluster admin privileges
When using self-signed certificates make sure you set `server.selfSignedCert` to true
or create a secret called `self-signed-certificate` in a target namespace with ca.crt holding your OpenShift router crt body.
When `server.selfSignedCert` the operator will create a test TLS route, GET it, extract certificate chain, convert to a secret `self-signed-certificate`,
and Che/CRW server will automatically add it to Java trust store.
#### K8S
......@@ -55,8 +59,8 @@ When enabling TLS, make sure you create a secret with crt and key, and let the O
## How to Configure
The operator watches all objects it creates and reconciles them with CR state. It means that if you edit a configMap **che**, the operator will revert changes.
Since not all Che configuration properties are custom resource spec fields (there are too many of them), the operator creates a second configMap called **custom**
which you can use for any environment variables not supported by CR. The operator will not reconcile configMap custom.
Since not all Che configuration properties are custom resource spec fields (there are simply too many of them), the operator creates a second configMap called **custom**
which you can use for any environment variables not supported by CR field. The operator will not reconcile configMap custom.
## How to Build Operator Image
......@@ -68,7 +72,7 @@ You can then use the resulting image in operator deployment (deploy/operator.yam
## Build and Deploy to a local cluster:
There's a little script that will build a Docker image and deploy an operator to a selected namespace,
There's a little script that will build a local Docker image and deploy an operator to a selected namespace,
as well as create service account, role, role binding, CRD and example CR.
```
......@@ -77,6 +81,8 @@ build_deploy_local.sh $namespace
```
The above method will work only with Docker 17.x (does not works if you want to build in MiniShift/MiniKube). Mostly useful if you run `oc cluster up` locally.
## How to Run/Debug Locally
You can run/debug this operator on your local machine (not deployed to a k8s cluster),
......
......@@ -14,17 +14,15 @@
BASE_DIR=$(cd "$(dirname "$0")"; pwd)
docker build -t che/operator .
#docker build -t che/operator .
kubectl apply -f ${BASE_DIR}/deploy/service_account.yaml -n=$1
kubectl apply -f ${BASE_DIR}/deploy/role.yaml -n=$1
kubectl apply -f ${BASE_DIR}/deploy/role_binding.yaml -n=$1
kubectl apply -f ${BASE_DIR}/deploy/crds/org_v1_che_crd.yaml -n=$1
# sometimes the operator cannot get CRD right away
sleep 2
# uncomment when on OpenShift if you need to use self signed certs and login with OpenShift in Che
# uncomment when on OpenShift if you need login with OpenShift in Che
#oc new-app -f ${BASE_DIR}/deploy/role_binding_oauth.yaml -p NAMESPACE=$1 -n=$1
#oc apply -f ${BASE_DIR}/deploy/cluster_role.yaml -n=$1
#oc new-app -f ${BASE_DIR}/deploy/role_binding_crt.yaml -p NAMESPACE=$1
#oc apply -f ${BASE_DIR}/deploy/role_get_tls.yaml
kubectl apply -f ${BASE_DIR}/deploy/operator-local.yaml -n=$1
kubectl apply -f ${BASE_DIR}/deploy/crds/org_v1_che_cr.yaml -n=$1
......@@ -13,18 +13,16 @@
BASE_DIR=$(cd "$(dirname "$0")"; pwd)
oc apply -f ${BASE_DIR}/service_account.yaml
oc apply -f ${BASE_DIR}/role.yaml
oc apply -f ${BASE_DIR}/role_binding.yaml
oc apply -f ${BASE_DIR}/crds/org_v1_che_crd.yaml
oc apply -f ${BASE_DIR}/deploy/service_account.yaml
oc apply -f ${BASE_DIR}/deploy/role.yaml
oc apply -f ${BASE_DIR}/deploy/role_binding.yaml
oc apply -f ${BASE_DIR}/deploy/crds/org_v1_che_crd.yaml
# sometimes the operator cannot get CRD right away
sleep 2
# uncomment if you need Login with OpenShift and/or use self signed certificates and tls
#oc new-app -f ${BASE_DIR}deploy/role_binding_oauth.yaml -p NAMESPACE=$1 -n=$1
#oc apply -f ${BASE_DIR}deploy/cluster_role.yaml -n=$1
#oc new-app -f ${BASE_DIR}deploy/role_binding_crt.yaml -p NAMESPACE=$1
#oc apply -f ${BASE_DIR}deploy/role_get_tls.yaml
# uncomment if you need Login with OpenShift
#oc new-app -f ${BASE_DIR}/deploy/role_binding_oauth.yaml -p NAMESPACE=$1 -n=$1
#oc apply -f ${BASE_DIR}/deploy/cluster_role.yaml -n=$1
oc apply -f ${BASE_DIR}/deploy/operator.yaml
oc apply -f ${BASE_DIR}/deploy/crds/org_v1_che_cr.yaml
\ No newline at end of file
......@@ -73,19 +73,19 @@ spec:
auth:
# instructs operator on whether or not to deploy Keycloak/RH SSO instance. When set to true provision connection details
externalKeycloak:
externalIdentityProvider: false
# retrieved from respective route/ingress unless explicitly specified in CR (when ExternalKeycloak is true)
keycloakURL: ''
identityProviderURL:
# password for keycloak database user. Auto generated if left blank
keycloakPostgresPassword: ''
# desired admin username of Keycloak admin user (applicable only when ExternalKeycloak is false)
keycloakAdminUserName: ''
# desired password of Keycloak admin user (applicable only when ExternalKeycloak is false)
keycloakAdminPassword: 'admin'
# name of a keycloak realm. This realm will be created, when ExternalKeycloak is true, otherwise passed to Che server
keycloakRealm: ''
# id of a keycloak client. This client will be created, when ExternalKeycloak is false, otherwise passed to Che server
keycloakClientId: ''
# desired admin username of Keycloak admin user (applicable only when externalIdentityProvider is false)
identityProviderAdminUserName: ''
# desired password of Keycloak admin user (applicable only when externalIdentityProvider is false)
identityProviderAdminPassword: 'admin'
# name of a keycloak realm. This realm will be created, when externalIdentityProvider is true, otherwise passed to Che server
identityProviderRealm: ''
# id of a keycloak client. This client will be created, when externalIdentityProvider is false, otherwise passed to Che server
identityProviderClientId: ''
# instructs an Operator to enable OpenShift v3 identity provider in Keycloak,
# as well as create respective oAuthClient and configure Che configMap accordingly
openShiftoAuth:
......
......@@ -22,4 +22,4 @@ spec:
scope: Namespaced
version: v1
subresources:
status: {}
status: {}
\ No newline at end of file
This diff is collapsed.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: checlusters.org.eclipse.che
spec:
group: org.eclipse.che
names:
kind: CheCluster
listKind: CheClusterList
plural: checlusters
singular: checluster
scope: Namespaced
version: v1
subresources:
status: {}
packageName: codeready-workspaces
channels:
- name: final
currentCSV: crwoperator.v1.1.0
#
# 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
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: null
name: che-operator
rules:
- apiGroups:
- extensions/v1beta1
resources:
- ingresses
verbs:
- "*"
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- "*"
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- "*"
- apiGroups:
- rbac.authorization.k8s.io
resources:
- clusterroles
- clusterrolebindings
verbs:
- "*"
- apiGroups:
- ""
resources:
- pods
- services
- serviceaccounts
- endpoints
- persistentvolumeclaims
- events
- configmaps
- secrets
- pods/exec
- pods/log
verbs:
- '*'
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- apiGroups:
- apps
resources:
- deployments
verbs:
- '*'
- apiGroups:
- monitoring.coreos.com
resources:
- servicemonitors
verbs:
- get
- create
- apiGroups:
- org.eclipse.che
resources:
- '*'
verbs:
- '*'
- apiGroups:
- extensions/v1beta1
resources:
- ingresses
verbs:
- '*'
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- clusterroles
- clusterrolebindings
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
- services
- serviceaccounts
- endpoints
- persistentvolumeclaims
- events
- configmaps
- secrets
- pods/exec
- pods/log
verbs:
- '*'
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- apiGroups:
- apps
resources:
- deployments
verbs:
- '*'
- apiGroups:
- monitoring.coreos.com
resources:
- servicemonitors
verbs:
- get
- create
- apiGroups:
- org.eclipse.che
resources:
- '*'
verbs:
- '*'
#
# 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
kind: Template
apiVersion: v1
metadata:
name: che-operator
objects:
- kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: che-operator
namespace: default
subjects:
- kind: ServiceAccount
name: che-operator
namespace: ${NAMESPACE}
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
parameters:
- name: NAMESPACE
displayName: Namespace
description: Namespace
required: true
\ No newline at end of file
#
# 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
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
#---
#kind: Role
#apiVersion: rbac.authorization.k8s.io/v1
#metadata:
# namespace: openshift-ingress
# name: secret-reader-4-0
#rules:
# - apiGroups: [""]
# resources: ["secrets"]
# verbs: ["get"]
\ No newline at end of file
......@@ -121,6 +121,11 @@ func newCheCluster() (cr *orgv1.CheCluster) {
TypeMeta: metav1.TypeMeta{
Kind: kind,
},
Spec:orgv1.CheClusterSpec{
Server:orgv1.CheClusterSpecServer{
SelfSignedCert: true,
},
},
}
return cr
}
......
......@@ -90,7 +90,7 @@ func main() {
logrus.Fatalf("Failed to create Operator deployment: %s", err)
}
logrus.Info("Waiting for CR Available status. Timeout 6 min")
logrus.Info("Waiting for CR Available status. Timeout 15 min")
deployed, err := VerifyCheRunning(che.AvailableStatus)
if deployed {
logrus.Info("Installation succeeded")
......@@ -165,7 +165,7 @@ func main() {
}
logrus.Infof("Checking if oauthclient %s has been created", oAuthClientName)
// verify oathclient name is set in che ConfigMap
// verify oAuthClient name is set in che ConfigMap
cm, err := getConfigMap("che")
if err != nil {
log.Fatalf("Failed to get ConfigMap: %s", err)
......
//
// 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 apis
import (
......
......@@ -83,19 +83,21 @@ type CheClusterSpecDB struct {
type CheClusterSpecAuth struct {
// ExternalKeycloak instructs operator on whether or not to deploy Keycloak/RH SSO instance. When set to true provision connection details
ExternalKeycloak bool `json:"externalKeycloak"`
// KeycloakURL is retrieved from respective route/ingress unless explicitly specified in CR (when ExternalKeycloak is true)
KeycloakURL string `json:"keycloakURL"`
// KeycloakAdminUserName is a desired admin username of Keycloak admin user (applicable only when ExternalKeycloak is false)
KeycloakAdminUserName string `json:"keycloakAdminUserName"`
// KeycloakAdminPassword is a desired password of Keycloak admin user (applicable only when ExternalKeycloak is false)
KeycloakAdminPassword string `json:"keycloakAdminPassword"`
// KeycloakRealm is name of a keycloak realm. When ExternalKeycloak is false this realm will be created, otherwise passed to Che server
KeycloakRealm string `json:"keycloakRealm"`
// KeycloakClientId is id of a keycloak client. When ExternalKeycloak is false this client will be created, otherwise passed to Che server
KeycloakClientId string `json:"keycloakClientId"`
ExternalKeycloak bool `json:"externalIdentityProvider"`
// KeycloakURL is retrieved from respective route/ingress unless explicitly specified in CR (when externalIdentityProvider is true)
KeycloakURL string `json:"identityProviderURL"`
// KeycloakURL is retrieved from respective route/ingress unless explicitly specified in CR (when externalIdentityProvider is true)
//IdentityProviderURL string `json:"identityProviderURL"`
// KeycloakAdminUserName is a desired admin username of Keycloak admin user (applicable only when externalIdentityProvider is false)
KeycloakAdminUserName string `json:"identityProviderAdminUserName"`
// KeycloakAdminPassword is a desired password of Keycloak admin user (applicable only when externalIdentityProvider is false)
KeycloakAdminPassword string `json:"identityProviderPassword"`
// KeycloakRealm is name of a keycloak realm. When externalIdentityProvider is false this realm will be created, otherwise passed to Che server
KeycloakRealm string `json:"identityProviderRealm"`
// KeycloakClientId is id of a keycloak client. When externalIdentityProvider is false this client will be created, otherwise passed to Che server
KeycloakClientId string `json:"identityProviderClientId"`
// KeycloakPostgresPassword is password for keycloak database user. Auto generated if left blank
KeycloakPostgresPassword string `json:"keycloakPostgresPassword"`
KeycloakPostgresPassword string `json:"identityProviderPostgresPassword"`
// UpdateAdminPassword forces the default admin Che user to update password on first login. False by default
UpdateAdminPassword bool `json:"updateAdminPassword"`
// OpenShiftOauth instructs an Operator to enable OpenShift v3 identity provider in Keycloak,
......@@ -106,10 +108,9 @@ type CheClusterSpecAuth struct {
// OauthSecret is secret used in oAuthClient. Auto generated if left blank
OauthSecret string `json:"oAuthSecret"`
// KeycloakImage is image:tag used in Keycloak deployment
KeycloakImage string `json:"keycloakImage"`
KeycloakImage string `json:"identityProviderImage"`
}
type CheClusterSpecStorage struct {
// PvcStrategy is a persistent volume claim strategy for Che server. Can be common (all workspaces PVCs in one volume),
// per-workspace (one PVC per workspace for all declared volumes) and unique (one PVC per declared volume). Defaults to common
......
......@@ -14,6 +14,7 @@ package che
import (
"context"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/deploy"
"github.com/eclipse/che-operator/pkg/util"
oauth "github.com/openshift/api/oauth/v1"
......@@ -189,10 +190,9 @@ type ReconcileChe struct {
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the CheCluster instance
tests := r.tests
var instance, err = r.GetCR(request)
instance, err := r.GetCR(request)
if err != nil {
if errors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
......@@ -208,7 +208,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
if err != nil {
logrus.Errorf("An error occurred when detecting current infra: %s", err)
}
// delete oAuthClient before CR is deleted
doInstallOpenShiftoAuthProvider := instance.Spec.Auth.OpenShiftOauth
if doInstallOpenShiftoAuthProvider {
......@@ -217,8 +216,9 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}
// create a secret with router tls cert
if isOpenShift {
// create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate
selfSignedCert := instance.Spec.Server.SelfSignedCert
if isOpenShift && selfSignedCert {
secret := &corev1.Secret{}
if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "self-signed-certificate", Namespace: instance.Namespace}, secret);
err != nil && errors.IsNotFound(err) {
......@@ -250,7 +250,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
// create service accounts:
// che is the one which token is used to create workspace objects
// che-workspace is SA used by plugins like exec and terminal with limited privileges like view and exec
// che-workspace is SA used by plugins like exec and terminal with limited privileges
cheServiceAccount := deploy.NewServiceAccount(instance, "che")
if err := r.CreateServiceAccount(instance, cheServiceAccount); err != nil {
return reconcile.Result{}, err
......@@ -402,6 +402,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
// create and provision Keycloak related objects
ExternalKeycloak := instance.Spec.Auth.ExternalKeycloak
if !ExternalKeycloak {
keycloakLabels := deploy.GetLabels(instance, "keycloak")
if err := r.CreateService(instance, "keycloak", keycloakLabels, "http", 8080); err != nil {
......
......@@ -331,12 +331,24 @@ func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request
keycloakPostgresPassword := util.GetValue(instance.Spec.Auth.KeycloakPostgresPassword, util.GeneratePasswd(12))
if len(instance.Spec.Auth.KeycloakPostgresPassword) < 1 {
instance.Spec.Auth.KeycloakPostgresPassword = keycloakPostgresPassword
keycloakDeployment, err := r.GetEffectiveDeployment(instance, "keycloak")
if err != nil {
logrus.Info("Disregard the error. No existing Identity provider deployment found. Generating passwd")
} else {
keycloakPostgresPassword = r.GetDeploymentEnv(keycloakDeployment, "DB_PASSWORD")
}
if err := r.UpdateCheCRSpec(instance, "auto-generated Keycloak DB password", "password-hidden"); err != nil {
return err
}
}
if len(instance.Spec.Auth.KeycloakAdminPassword) < 1 {
keycloakAdminPassword := util.GetValue(instance.Spec.Auth.KeycloakAdminPassword, util.GeneratePasswd(12))
keycloakDeployment, err := r.GetEffectiveDeployment(instance, "keycloak")
if err != nil {
logrus.Info("Disregard the error. No existing Identity provider deployment found. Generating passwd")
} else {
keycloakAdminPassword = r.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_PASSWORD")
}
instance.Spec.Auth.KeycloakAdminPassword = keycloakAdminPassword
if err := r.UpdateCheCRSpec(instance, "Keycloak admin password", "password hidden"); err != nil {
return err
......@@ -344,8 +356,14 @@ func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request
}
if len(instance.Spec.Auth.KeycloakAdminUserName) < 1 {
keycloakAdminUserName := util.GetValue(instance.Spec.Auth.KeycloakAdminUserName, "admin")
keycloakDeployment, err := r.GetEffectiveDeployment(instance, "keycloak")
if err != nil {
logrus.Info("Disregard the error. No existing Identity provider deployment found. Generating admin username")
} else {
keycloakAdminUserName = r.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_USERNAME")
}
instance.Spec.Auth.KeycloakAdminUserName = keycloakAdminUserName
if err := r.UpdateCheCRSpec(instance, "Keycloak admin username", "password hidden"); err != nil {
if err := r.UpdateCheCRSpec(instance, "Keycloak admin username", keycloakAdminUserName); err != nil {
return err
}
}
......@@ -406,6 +424,12 @@ func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request
keycloakImage := util.GetValue(instance.Spec.Auth.KeycloakImage, defaultKeycloakImage)
if len(instance.Spec.Auth.KeycloakImage) < 1 {
instance.Spec.Auth.KeycloakImage = keycloakImage
keycloakDeployment, err := r.GetEffectiveDeployment(instance, "keycloak")
if err != nil {
logrus.Info("Disregard the error. No existing Identity provider deployment found. Using default image")
} else {
keycloakImage = keycloakDeployment.Spec.Template.Spec.Containers[0].Image
}
if err := r.UpdateCheCRSpec(instance, "Keycloak image:tag", keycloakImage); err != nil {
return err
}
......@@ -420,6 +444,8 @@ func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request
keycloakClientId := util.GetValue(instance.Spec.Auth.KeycloakClientId, cheFlavor+"-public")
if len(instance.Spec.Auth.KeycloakClientId) < 1 {
instance.Spec.Auth.KeycloakClientId = keycloakClientId
if err := r.UpdateCheCRSpec(instance, "Keycloak client ID", keycloakClientId); err != nil {
return err
}
......
......@@ -83,3 +83,15 @@ func (r *ReconcileChe) GetOAuthClient(oAuthClientName string) (oAuthClient *oaut
}
return oAuthClient, nil
}
func (r *ReconcileChe)GetDeploymentEnv(deployment *appsv1.Deployment, key string) (value string) {
env := deployment.Spec.Template.Spec.Containers[0].Env
for i := range env {
name := env[i].Name
if name == key {
value = env[i].Value
break
}
}
return value
}
\ No newline at end of file
......@@ -38,15 +38,6 @@ func (r *ReconcileChe) UpdateCheCRStatus(instance *orgv1.CheCluster, updatedFiel
return nil
}
func (r *ReconcileChe) UpdateCheCRSpec1(instance *orgv1.CheCluster, updatedField string