feat: Allow to configure user custom roles (#1663)
* feat: Allow to configure user custom roles without duplicating default ones Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/1670/head
parent
077a358355
commit
4bcc78a27a
|
|
@ -364,6 +364,9 @@ func TestConvertFrom(t *testing.T) {
|
|||
SecondsOfInactivityBeforeIdling: pointer.Int32Ptr(1800),
|
||||
SecondsOfRunBeforeIdling: pointer.Int32Ptr(-1),
|
||||
MaxNumberOfRunningWorkspacesPerUser: pointer.Int64Ptr(10),
|
||||
User: &chev2.UserConfiguration{
|
||||
ClusterRoles: []string{"ClusterRoles_1", "ClusterRoles_2"},
|
||||
},
|
||||
},
|
||||
ContainerRegistry: chev2.CheClusterContainerRegistry{
|
||||
Hostname: "AirGapContainerRegistryHostname",
|
||||
|
|
@ -506,6 +509,7 @@ func TestConvertFrom(t *testing.T) {
|
|||
},
|
||||
})
|
||||
assert.Equal(t, checlusterv1.Spec.Server.WorkspacesDefaultPlugins, []chev1.WorkspacesDefaultPlugins{{Editor: "Editor", Plugins: []string{"Plugins_1", "Plugins_2"}}})
|
||||
assert.Equal(t, checlusterv1.Spec.Server.CheWorkspaceClusterRole, "ClusterRoles_1,ClusterRoles_2")
|
||||
|
||||
assert.Equal(t, checlusterv1.Spec.Storage.PvcStrategy, "PvcStrategy")
|
||||
assert.Equal(t, checlusterv1.Spec.Storage.PvcClaimSize, "StorageClaimSize")
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ func TestConvertTo(t *testing.T) {
|
|||
CheLogLevel: "CheLogLevel",
|
||||
CheDebug: "true",
|
||||
CheClusterRoles: "CheClusterRoles_1,CheClusterRoles_2",
|
||||
CheWorkspaceClusterRole: "CheWorkspaceClusterRole",
|
||||
CheWorkspaceClusterRole: "ClusterRoles_1,ClusterRoles_2",
|
||||
WorkspaceNamespaceDefault: "WorkspaceNamespaceDefault",
|
||||
AllowAutoProvisionUserNamespace: pointer.BoolPtr(true),
|
||||
WorkspaceDefaultEditor: "WorkspaceDefaultEditor",
|
||||
|
|
@ -413,6 +413,7 @@ func TestConvertTo(t *testing.T) {
|
|||
Editor: "Editor",
|
||||
Plugins: []string{"Plugin_1,Plugin_2"},
|
||||
}})
|
||||
assert.Equal(t, checlusterv2.Spec.DevEnvironments.User.ClusterRoles, []string{"CheClusterRoles_1", "CheClusterRoles_2"})
|
||||
|
||||
assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Name, defaults.GetCheFlavor()+"-dashboard")
|
||||
assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Image, "DashboardImage")
|
||||
|
|
|
|||
|
|
@ -281,6 +281,12 @@ func TestRoundConvertCheClusterV2(t *testing.T) {
|
|||
Effect: "Effect",
|
||||
}},
|
||||
MaxNumberOfRunningWorkspacesPerUser: pointer.Int64Ptr(10),
|
||||
User: &chev2.UserConfiguration{
|
||||
ClusterRoles: []string{
|
||||
"ClusterRoles_1",
|
||||
"ClusterRoles_2",
|
||||
},
|
||||
},
|
||||
},
|
||||
ContainerRegistry: chev2.CheClusterContainerRegistry{
|
||||
Hostname: "AirGapContainerRegistryHostname",
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ func (dst *CheCluster) convertFrom_Server(src *chev2.CheCluster) error {
|
|||
dst.Spec.Server.AirGapContainerRegistryHostname = src.Spec.ContainerRegistry.Hostname
|
||||
dst.Spec.Server.AirGapContainerRegistryOrganization = src.Spec.ContainerRegistry.Organization
|
||||
dst.Spec.Server.CheClusterRoles = strings.Join(src.Spec.Components.CheServer.ClusterRoles, ",")
|
||||
if src.Spec.DevEnvironments.User != nil {
|
||||
dst.Spec.Server.CheWorkspaceClusterRole = strings.Join(src.Spec.DevEnvironments.User.ClusterRoles, ",")
|
||||
}
|
||||
dst.Spec.Server.CustomCheProperties = utils.CloneMap(src.Spec.Components.CheServer.ExtraProperties)
|
||||
if src.Spec.Components.CheServer.Debug != nil {
|
||||
dst.Spec.Server.CheDebug = strconv.FormatBool(*src.Spec.Components.CheServer.Debug)
|
||||
|
|
|
|||
|
|
@ -162,6 +162,11 @@ func (src *CheCluster) convertTo_DevEnvironments(dst *chev2.CheCluster) error {
|
|||
dst.Spec.DevEnvironments.DefaultComponents = src.Spec.Server.WorkspaceDefaultComponents
|
||||
dst.Spec.DevEnvironments.SecondsOfInactivityBeforeIdling = src.Spec.DevWorkspace.SecondsOfInactivityBeforeIdling
|
||||
dst.Spec.DevEnvironments.SecondsOfRunBeforeIdling = src.Spec.DevWorkspace.SecondsOfRunBeforeIdling
|
||||
if src.Spec.Server.CheWorkspaceClusterRole != "" {
|
||||
dst.Spec.DevEnvironments.User = &chev2.UserConfiguration{
|
||||
ClusterRoles: strings.Split(src.Spec.Server.CheClusterRoles, ","),
|
||||
}
|
||||
}
|
||||
|
||||
if err := src.convertTo_Workspaces_Storage(dst); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -148,6 +148,9 @@ type CheClusterDevEnvironments struct {
|
|||
// +kubebuilder:validation:Minimum:=-1
|
||||
// +optional
|
||||
MaxNumberOfRunningWorkspacesPerUser *int64 `json:"maxNumberOfRunningWorkspacesPerUser,omitempty"`
|
||||
// User configuration.
|
||||
// +optional
|
||||
User *UserConfiguration `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// Che components configuration.
|
||||
|
|
@ -246,13 +249,13 @@ type CheServer struct {
|
|||
// +optional
|
||||
// +kubebuilder:default:=false
|
||||
Debug *bool `json:"debug,omitempty"`
|
||||
// ClusterRoles assigned to Che ServiceAccount.
|
||||
// The defaults roles are:
|
||||
// - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
// - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
// - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
// where the <che-namespace> is the namespace where the CheCluster CRD is created.
|
||||
// Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
// Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label.
|
||||
// The defaults roles are:
|
||||
// - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
// - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
// - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
// where the <che-namespace> is the namespace where the CheCluster CR is created.
|
||||
// The Che Operator must already have all permissions in these ClusterRoles to grant them.
|
||||
// +optional
|
||||
ClusterRoles []string `json:"clusterRoles,omitempty"`
|
||||
|
|
@ -411,6 +414,13 @@ type TrustedCerts struct {
|
|||
GitTrustedCertsConfigMapName string `json:"gitTrustedCertsConfigMapName,omitempty"`
|
||||
}
|
||||
|
||||
type UserConfiguration struct {
|
||||
// Additional ClusterRoles assigned to the user.
|
||||
// The role must have `app.kubernetes.io/part-of=che.eclipse.org` label.
|
||||
// +optional
|
||||
ClusterRoles []string `json:"clusterRoles,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration settings related to the workspaces persistent storage.
|
||||
type WorkspaceStorage struct {
|
||||
// PVC settings when using the `per-user` PVC strategy.
|
||||
|
|
|
|||
|
|
@ -223,6 +223,11 @@ func (in *CheClusterDevEnvironments) DeepCopyInto(out *CheClusterDevEnvironments
|
|||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.User != nil {
|
||||
in, out := &in.User, &out.User
|
||||
*out = new(UserConfiguration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterDevEnvironments.
|
||||
|
|
@ -883,6 +888,26 @@ func (in *TrustedCerts) DeepCopy() *TrustedCerts {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UserConfiguration) DeepCopyInto(out *UserConfiguration) {
|
||||
*out = *in
|
||||
if in.ClusterRoles != nil {
|
||||
in, out := &in.ClusterRoles, &out.ClusterRoles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserConfiguration.
|
||||
func (in *UserConfiguration) DeepCopy() *UserConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UserConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *WorkspaceDefaultPlugins) DeepCopyInto(out *WorkspaceDefaultPlugins) {
|
||||
*out = *in
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ metadata:
|
|||
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
|
||||
repository: https://github.com/eclipse-che/che-operator
|
||||
support: Eclipse Foundation
|
||||
name: eclipse-che.v7.64.0-786.next
|
||||
name: eclipse-che.v7.65.0-789.next
|
||||
namespace: placeholder
|
||||
spec:
|
||||
apiservicedefinitions: {}
|
||||
|
|
@ -1243,7 +1243,7 @@ spec:
|
|||
minKubeVersion: 1.19.0
|
||||
provider:
|
||||
name: Eclipse Foundation
|
||||
version: 7.64.0-786.next
|
||||
version: 7.65.0-789.next
|
||||
webhookdefinitions:
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
|
|
|
|||
|
|
@ -4067,13 +4067,14 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
CR is created. The Che Operator must already have all
|
||||
permissions in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
|
@ -7250,6 +7251,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -3938,12 +3938,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7051,6 +7052,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -100,9 +100,7 @@ func NewReconciler(
|
|||
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
|
||||
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
|
||||
reconcileManager.RegisterReconciler(devworkspaceconfig.NewDevWorkspaceConfigReconciler())
|
||||
reconcileManager.RegisterReconciler(rbac.NewCheServerPermissionsReconciler())
|
||||
reconcileManager.RegisterReconciler(rbac.NewGatewayPermissionsReconciler())
|
||||
reconcileManager.RegisterReconciler(rbac.NewWorkspacePermissionsReconciler())
|
||||
|
||||
// we have to expose che endpoint independently of syncing other server
|
||||
// resources since che host is used for dashboard deployment and che config map
|
||||
|
|
|
|||
|
|
@ -3957,12 +3957,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7070,6 +7071,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -3952,12 +3952,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7065,6 +7066,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -3957,12 +3957,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7070,6 +7071,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -3952,12 +3952,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7065,6 +7066,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -3952,12 +3952,13 @@ spec:
|
|||
server.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: 'ClusterRoles assigned to Che ServiceAccount.
|
||||
The defaults roles are: - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-clusterrole` - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
description: 'Additional ClusterRoles assigned to Che ServiceAccount.
|
||||
Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The defaults roles are: - `<che-namespace>-cheworkspaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-namespaces-clusterrole`
|
||||
- `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
|
||||
where the <che-namespace> is the namespace where the CheCluster
|
||||
CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label. The Che Operator must already have all permissions
|
||||
CR is created. The Che Operator must already have all permissions
|
||||
in these ClusterRoles to grant them.'
|
||||
items:
|
||||
type: string
|
||||
|
|
@ -7065,6 +7066,17 @@ spec:
|
|||
label.'
|
||||
type: string
|
||||
type: object
|
||||
user:
|
||||
description: User configuration.
|
||||
properties:
|
||||
clusterRoles:
|
||||
description: Additional ClusterRoles assigned to the user.
|
||||
The role must have `app.kubernetes.io/part-of=che.eclipse.org`
|
||||
label.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
gitServices:
|
||||
description: A configuration that allows users to work with remote
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ const (
|
|||
CheFlavor = "che"
|
||||
CheEclipseOrg = "che.eclipse.org"
|
||||
InstallOrUpdateFailed = "InstallOrUpdateFailed"
|
||||
FinalizerSuffix = "finalizers.che.eclipse.org"
|
||||
|
||||
// DevWorkspace
|
||||
DevWorkspaceServiceAccountName = "devworkspace-controller-serviceaccount"
|
||||
|
|
|
|||
|
|
@ -37,9 +37,6 @@ var (
|
|||
defaultSingleHostGatewayConfigSidecarImage string
|
||||
defaultGatewayAuthenticationSidecarImage string
|
||||
defaultGatewayAuthorizationSidecarImage string
|
||||
defaultCheWorkspacePluginBrokerMetadataImage string
|
||||
defaultCheWorkspacePluginBrokerArtifactsImage string
|
||||
defaultCheServerSecureExposerJwtProxyImage string
|
||||
defaultConsoleLinkName string
|
||||
defaultConsoleLinkDisplayName string
|
||||
defaultConsoleLinkSection string
|
||||
|
|
@ -160,30 +157,6 @@ func GetDevfileRegistryImage(checluster interface{}) string {
|
|||
return PatchDefaultImageName(checluster, defaultDevfileRegistryImage)
|
||||
}
|
||||
|
||||
func GetCheWorkspacePluginBrokerMetadataImage(checluster interface{}) string {
|
||||
if !initialized {
|
||||
logrus.Fatalf("Operator defaults are not initialized.")
|
||||
}
|
||||
|
||||
return PatchDefaultImageName(checluster, defaultCheWorkspacePluginBrokerMetadataImage)
|
||||
}
|
||||
|
||||
func GetCheWorkspacePluginBrokerArtifactsImage(checluster interface{}) string {
|
||||
if !initialized {
|
||||
logrus.Fatalf("Operator defaults are not initialized.")
|
||||
}
|
||||
|
||||
return PatchDefaultImageName(checluster, defaultCheWorkspacePluginBrokerArtifactsImage)
|
||||
}
|
||||
|
||||
func GetCheServerSecureExposerJwtProxyImage(checluster interface{}) string {
|
||||
if !initialized {
|
||||
logrus.Fatalf("Operator defaults are not initialized.")
|
||||
}
|
||||
|
||||
return PatchDefaultImageName(checluster, defaultCheServerSecureExposerJwtProxyImage)
|
||||
}
|
||||
|
||||
func GetGatewayImage(checluster interface{}) string {
|
||||
if !initialized {
|
||||
logrus.Fatalf("Operator defaults are not initialized.")
|
||||
|
|
|
|||
|
|
@ -212,6 +212,12 @@ func CloneMap(m map[string]string) map[string]string {
|
|||
return result
|
||||
}
|
||||
|
||||
func AddMap(a map[string]string, b map[string]string) {
|
||||
for k, v := range b {
|
||||
a[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Converts label map into plain string
|
||||
func FormatLabels(m map[string]string) string {
|
||||
if len(m) == 0 {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
|
||||
|
|
@ -21,7 +19,6 @@ import (
|
|||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var ClusterRoleBindingDiffOpts = cmp.Options{
|
||||
|
|
@ -38,39 +35,6 @@ func SyncClusterRoleBindingToCluster(
|
|||
return Sync(deployContext, crbSpec, ClusterRoleBindingDiffOpts)
|
||||
}
|
||||
|
||||
func SyncClusterRoleBindingAndAddFinalizerToCluster(
|
||||
deployContext *chetypes.DeployContext,
|
||||
name string,
|
||||
serviceAccountName string,
|
||||
clusterRoleName string) (bool, error) {
|
||||
|
||||
finalizer := GetFinalizerName(strings.ToLower(name) + ".crb")
|
||||
crbSpec := getClusterRoleBindingSpec(deployContext, name, serviceAccountName, deployContext.CheCluster.Namespace, clusterRoleName)
|
||||
return SyncAndAddFinalizer(deployContext, crbSpec, ClusterRoleBindingDiffOpts, finalizer)
|
||||
}
|
||||
|
||||
func ReconcileClusterRoleBindingFinalizer(deployContext *chetypes.DeployContext, name string) error {
|
||||
if deployContext.CheCluster.DeletionTimestamp.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
finalizer := GetFinalizerName(strings.ToLower(name) + ".crb")
|
||||
return DeleteObjectWithFinalizer(deployContext, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}, finalizer)
|
||||
}
|
||||
|
||||
func GetLegacyUniqueClusterRoleBindingName(deployContext *chetypes.DeployContext, serviceAccount string, clusterRole string) string {
|
||||
return deployContext.CheCluster.Namespace + "-" + serviceAccount + "-" + clusterRole
|
||||
}
|
||||
|
||||
func ReconcileLegacyClusterRoleBindingFinalizer(deployContext *chetypes.DeployContext, name string) error {
|
||||
if deployContext.CheCluster.DeletionTimestamp.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
finalizer := strings.ToLower(name) + ".clusterrolebinding.finalizers.che.eclipse.org"
|
||||
return DeleteObjectWithFinalizer(deployContext, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}, finalizer)
|
||||
}
|
||||
|
||||
func getClusterRoleBindingSpec(
|
||||
deployContext *chetypes.DeployContext,
|
||||
name string,
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@ package deploy
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
|
@ -71,63 +69,3 @@ func TestSyncClusterRoleBindingToCluster(t *testing.T) {
|
|||
t.Fatalf("Failed to sync crb: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncClusterRoleBindingAndAddFinalizerToCluster(t *testing.T) {
|
||||
chev2.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
deployContext := &chetypes.DeployContext{
|
||||
CheCluster: &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
ClusterAPI: chetypes.ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachingClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
cli.Create(context.TODO(), deployContext.CheCluster)
|
||||
|
||||
done, err := SyncClusterRoleBindingAndAddFinalizerToCluster(deployContext, "test", "sa", "clusterrole-1")
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync crb: %v", err)
|
||||
}
|
||||
|
||||
if !utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") {
|
||||
t.Fatalf("Failed to add finalizer")
|
||||
}
|
||||
|
||||
// don't expect any deletion
|
||||
err = ReconcileClusterRoleBindingFinalizer(deployContext, "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reconcile finalizer: %v", err)
|
||||
}
|
||||
|
||||
actual := &rbacv1.ClusterRoleBinding{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("CRD shouldn't be deleted: %v", err)
|
||||
}
|
||||
if !utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") {
|
||||
t.Fatalf("Finalizer shouldn't be deleted")
|
||||
}
|
||||
|
||||
// crb must be deleted as well as finalizer
|
||||
deployContext.CheCluster.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
err = ReconcileClusterRoleBindingFinalizer(deployContext, "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reconcile finalizer: %v", err)
|
||||
}
|
||||
|
||||
actual = &rbacv1.ClusterRoleBinding{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to remove crb: %v", err)
|
||||
}
|
||||
if utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") {
|
||||
t.Fatalf("Failed to remove finalizer")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func CleanUpAllFinalizers(ctx *chetypes.DeployContext) error {
|
||||
ctx.CheCluster.Finalizers = []string{}
|
||||
return ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster)
|
||||
}
|
||||
|
||||
func AppendFinalizer(deployContext *chetypes.DeployContext, finalizer string) error {
|
||||
if err := ReloadCheClusterCR(deployContext); err != nil {
|
||||
return err
|
||||
|
|
@ -78,12 +83,3 @@ func DeleteObjectWithFinalizer(deployContext *chetypes.DeployContext, key client
|
|||
|
||||
return DeleteFinalizer(deployContext, finalizer)
|
||||
}
|
||||
|
||||
func GetFinalizerName(prefix string) string {
|
||||
finalizer := prefix + ".finalizers.che.eclipse.org"
|
||||
diff := len(finalizer) - 63
|
||||
if diff > 0 {
|
||||
return finalizer[:len(finalizer)-diff]
|
||||
}
|
||||
return finalizer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,13 +99,3 @@ func TestDeleteFinalizer(t *testing.T) {
|
|||
t.Fatalf("Failed to delete finalizer: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFinalizerNameShouldReturnStringLess64Chars(t *testing.T) {
|
||||
expected := "7890123456789012345678901234567891234567.finalizers.che.eclipse"
|
||||
prefix := "7890123456789012345678901234567891234567"
|
||||
|
||||
actual := GetFinalizerName(prefix)
|
||||
if expected != actual {
|
||||
t.Fatalf("Incorrect finalizer name: %s", actual)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2012-2021 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 rbac
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
type CheServerPermissionsReconciler struct {
|
||||
deploy.Reconcilable
|
||||
}
|
||||
|
||||
func NewCheServerPermissionsReconciler() *CheServerPermissionsReconciler {
|
||||
return &CheServerPermissionsReconciler{}
|
||||
}
|
||||
|
||||
func (c *CheServerPermissionsReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
|
||||
// Create service account "che" for che-server component.
|
||||
// "che" is the one which token is used to create workspace objects.
|
||||
// Notice: Also we have on more "che-workspace" SA used by plugins like exec, terminal, metrics with limited privileges.
|
||||
done, err := deploy.SyncServiceAccountToCluster(ctx, constants.DefaultCheServiceAccountName)
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true}, false, err
|
||||
}
|
||||
|
||||
for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
|
||||
cheClusterRole := strings.TrimSpace(cheClusterRole)
|
||||
if cheClusterRole != "" {
|
||||
cheClusterRoleBindingName := cheClusterRole
|
||||
done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(ctx, cheClusterRoleBindingName, constants.DefaultCheServiceAccountName, cheClusterRole)
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true}, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, err
|
||||
}
|
||||
|
||||
func (c *CheServerPermissionsReconciler) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
done := true
|
||||
|
||||
for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
|
||||
cheClusterRole := strings.TrimSpace(cheClusterRole)
|
||||
if cheClusterRole != "" {
|
||||
cheClusterRoleBindingName := cheClusterRole
|
||||
if err := deploy.ReconcileClusterRoleBindingFinalizer(ctx, cheClusterRoleBindingName); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
}
|
||||
|
||||
// Removes any legacy CRB https://github.com/eclipse/che/issues/19506
|
||||
cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(ctx, constants.DefaultCheServiceAccountName, cheClusterRole)
|
||||
if err := deploy.ReconcileLegacyClusterRoleBindingFinalizer(ctx, cheClusterRoleBindingName); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/sirupsen/logrus"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const (
|
||||
// CheNamespaceEditorClusterRoleNameTemplate - manage namespaces "cluster role" and "clusterrolebinding" template name
|
||||
CheNamespaceEditorClusterRoleNameTemplate = "%s-cheworkspaces-namespaces-clusterrole"
|
||||
// CheWorkspacesClusterRoleNameTemplate - manage workspaces "cluster role" and "clusterrolebinding" template name
|
||||
CheWorkspacesClusterRoleNameTemplate = "%s-cheworkspaces-clusterrole"
|
||||
// DevWorkspaceClusterRoleNameTemplate - manage DevWorkspace "cluster role" and "clusterrolebinding" template name
|
||||
DevWorkspaceClusterRoleNameTemplate = "%s-cheworkspaces-devworkspace-clusterrole"
|
||||
|
||||
CheWorkspacesClusterPermissionsFinalizerName = "cheWorkspaces.clusterpermissions.finalizers.che.eclipse.org"
|
||||
NamespacesEditorPermissionsFinalizerName = "namespaces-editor.permissions.finalizers.che.eclipse.org"
|
||||
DevWorkspacePermissionsFinalizerName = "devWorkspace.permissions.finalizers.che.eclipse.org"
|
||||
)
|
||||
|
||||
type WorkspacePermissionsReconciler struct {
|
||||
deploy.Reconcilable
|
||||
}
|
||||
|
||||
func NewWorkspacePermissionsReconciler() *WorkspacePermissionsReconciler {
|
||||
return &WorkspacePermissionsReconciler{}
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
|
||||
done, err := wp.delegateWorkspacePermissionsInTheDifferNamespaceThanChe(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true}, false, err
|
||||
}
|
||||
|
||||
done, err = wp.delegateNamespaceEditorPermissions(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true}, false, err
|
||||
}
|
||||
|
||||
done, err = wp.delegateDevWorkspacePermissions(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true}, false, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
done := true
|
||||
|
||||
if completed := wp.removeNamespaceEditorPermissions(ctx); !completed {
|
||||
done = false
|
||||
}
|
||||
|
||||
if completed := wp.removeDevWorkspacePermissions(ctx); !completed {
|
||||
done = false
|
||||
}
|
||||
|
||||
if completed := wp.removeWorkspacePermissionsInTheDifferNamespaceThanChe(ctx); !completed {
|
||||
done = false
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
// Create cluster roles and cluster role bindings for "che" service account.
|
||||
// che-server uses "che" service account for creation new workspaces and workspace components.
|
||||
// Operator will create two cluster roles:
|
||||
// - "<workspace-namespace/project-name>-cheworkspaces-namespaces-clusterrole" - cluster role to manage namespace(for Kubernetes platform)
|
||||
// or project(for Openshift platform) for new workspace.
|
||||
// - "<workspace-namespace/project-name>-cheworkspaces-clusterrole" - cluster role to create and manage k8s objects required for
|
||||
// workspace components.
|
||||
// Notice: After permission delegation che-server will create service account "che-workspace" ITSELF with
|
||||
// "exec" and "view" roles for each new workspace.
|
||||
func (wp *WorkspacePermissionsReconciler) delegateWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *chetypes.DeployContext) (bool, error) {
|
||||
сheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
сheWorkspacesClusterRoleBindingName := сheWorkspacesClusterRoleName
|
||||
|
||||
// Create clusterrole +kubebuilder:storageversion"<workspace-namespace/project-name>-cheworkspaces-namespaces-clusterrole" to create k8s components for Che workspaces.
|
||||
done, err := deploy.SyncClusterRoleToCluster(deployContext, сheWorkspacesClusterRoleName, wp.getWorkspacesPolicies())
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, constants.DefaultCheServiceAccountName, сheWorkspacesClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = deploy.AppendFinalizer(deployContext, CheWorkspacesClusterPermissionsFinalizerName)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) removeWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *chetypes.DeployContext) bool {
|
||||
done := true
|
||||
|
||||
cheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
cheWorkspacesClusterRoleBindingName := cheWorkspacesClusterRoleName
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: cheWorkspacesClusterRoleName}, &rbacv1.ClusterRole{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete Che Workspaces ClusterRole '%s', cause: %v", cheWorkspacesClusterRoleName, err)
|
||||
}
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: cheWorkspacesClusterRoleBindingName}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete Che Workspace ClusterRoleBinding '%s', cause: %v", cheWorkspacesClusterRoleBindingName, err)
|
||||
}
|
||||
|
||||
if err := deploy.DeleteFinalizer(deployContext, CheWorkspacesClusterPermissionsFinalizerName); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) delegateNamespaceEditorPermissions(deployContext *chetypes.DeployContext) (bool, error) {
|
||||
сheNamespaceEditorClusterRoleName := fmt.Sprintf(CheNamespaceEditorClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
сheNamespaceEditorClusterRoleBindingName := сheNamespaceEditorClusterRoleName
|
||||
|
||||
// Create clusterrole "<workspace-namespace/project-name>-clusterrole-manage-namespaces" to manage namespace/projects for Che workspaces.
|
||||
done, err := deploy.SyncClusterRoleToCluster(deployContext, сheNamespaceEditorClusterRoleName, wp.getNamespaceEditorPolicies())
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheNamespaceEditorClusterRoleBindingName, constants.DefaultCheServiceAccountName, сheNamespaceEditorClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = deploy.AppendFinalizer(deployContext, NamespacesEditorPermissionsFinalizerName)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) removeNamespaceEditorPermissions(deployContext *chetypes.DeployContext) bool {
|
||||
done := true
|
||||
|
||||
cheNamespaceEditorClusterRoleName := fmt.Sprintf(CheNamespaceEditorClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: cheNamespaceEditorClusterRoleName}, &rbacv1.ClusterRole{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete Editor ClusterRole '%s', cause: %v", cheNamespaceEditorClusterRoleName, err)
|
||||
}
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: cheNamespaceEditorClusterRoleName}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete Editor ClusterRoleBinding '%s', cause: %v", cheNamespaceEditorClusterRoleName, err)
|
||||
}
|
||||
|
||||
if err := deploy.DeleteFinalizer(deployContext, NamespacesEditorPermissionsFinalizerName); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) delegateDevWorkspacePermissions(deployContext *chetypes.DeployContext) (bool, error) {
|
||||
devWorkspaceClusterRoleName := fmt.Sprintf(DevWorkspaceClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
devWorkspaceClusterRoleBindingName := devWorkspaceClusterRoleName
|
||||
|
||||
done, err := deploy.SyncClusterRoleToCluster(deployContext, devWorkspaceClusterRoleName, wp.getDevWorkspacePolicies())
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, devWorkspaceClusterRoleBindingName, constants.DefaultCheServiceAccountName, devWorkspaceClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = deploy.AppendFinalizer(deployContext, DevWorkspacePermissionsFinalizerName)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) removeDevWorkspacePermissions(deployContext *chetypes.DeployContext) bool {
|
||||
done := true
|
||||
|
||||
devWorkspaceClusterRoleName := fmt.Sprintf(DevWorkspaceClusterRoleNameTemplate, deployContext.CheCluster.Namespace)
|
||||
devWorkspaceClusterRoleBindingName := devWorkspaceClusterRoleName
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: devWorkspaceClusterRoleName}, &rbacv1.ClusterRole{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete DevWorkspace ClusterRole '%s', cause: %v", devWorkspaceClusterRoleName, err)
|
||||
}
|
||||
|
||||
if _, err := deploy.Delete(deployContext, types.NamespacedName{Name: devWorkspaceClusterRoleBindingName}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete DevWorkspace ClusterRoleBinding '%s', cause: %v", devWorkspaceClusterRoleName, err)
|
||||
}
|
||||
|
||||
if err := deploy.DeleteFinalizer(deployContext, DevWorkspacePermissionsFinalizerName); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) getDevWorkspacePolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"workspace.devfile.io"},
|
||||
Resources: []string{"devworkspaces", "devworkspacetemplates"},
|
||||
Verbs: []string{"get", "create", "delete", "list", "update", "patch", "watch"},
|
||||
},
|
||||
}
|
||||
|
||||
return k8sPolicies
|
||||
}
|
||||
|
||||
func (wp *WorkspacePermissionsReconciler) getNamespaceEditorPolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
Verbs: []string{"get", "create", "update", "list"},
|
||||
},
|
||||
}
|
||||
|
||||
openshiftPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projectrequests"},
|
||||
Verbs: []string{"create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projects"},
|
||||
Verbs: []string{"get", "list"},
|
||||
},
|
||||
}
|
||||
|
||||
if infrastructure.IsOpenShift() {
|
||||
return append(k8sPolicies, openshiftPolicies...)
|
||||
}
|
||||
return k8sPolicies
|
||||
}
|
||||
|
||||
func (c *WorkspacePermissionsReconciler) getWorkspacesPolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"serviceaccounts"},
|
||||
Verbs: []string{"get", "watch", "create"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods/exec"},
|
||||
Verbs: []string{"get", "create"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods/log"},
|
||||
Verbs: []string{"get", "list", "watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"persistentvolumeclaims"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"services"},
|
||||
Verbs: []string{"get", "list", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"configmaps"},
|
||||
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"events"},
|
||||
Verbs: []string{"watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"list"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"deployments"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"replicasets"},
|
||||
Verbs: []string{"get", "list", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"extensions"},
|
||||
Resources: []string{"ingresses"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"networking.k8s.io"},
|
||||
Resources: []string{"ingresses"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"roles"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"rolebindings"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"metrics.k8s.io"},
|
||||
Resources: []string{"pods", "nodes"},
|
||||
Verbs: []string{"get", "list", "watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
Verbs: []string{"get", "list"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"events"},
|
||||
Verbs: []string{"watch", "list"},
|
||||
},
|
||||
}
|
||||
openshiftPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"route.openshift.io"},
|
||||
Resources: []string{"routes"},
|
||||
Verbs: []string{"get", "list", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"authorization.openshift.io"},
|
||||
Resources: []string{"roles"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"authorization.openshift.io"},
|
||||
Resources: []string{"rolebindings"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projects"},
|
||||
Verbs: []string{"get"},
|
||||
},
|
||||
}
|
||||
|
||||
if infrastructure.IsOpenShift() {
|
||||
return append(k8sPolicies, openshiftPolicies...)
|
||||
}
|
||||
return k8sPolicies
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/test"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func TestReconcileWorkspacePermissions(t *testing.T) {
|
||||
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
initObjects []runtime.Object
|
||||
checluster *chev2.CheCluster
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "che-operator should delegate permission for workspaces in differ namespace than Che. WorkspaceNamespaceDefault = 'some-test-namespace'",
|
||||
initObjects: []runtime.Object{},
|
||||
checluster: &chev2.CheCluster{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
DevEnvironments: chev2.CheClusterDevEnvironments{
|
||||
DefaultNamespace: chev2.DefaultNamespace{
|
||||
Template: "some-test-namespace",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "che-operator should delegate permission for workspaces in differ namespace than Che. Property CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT = 'some-test-namespace'",
|
||||
initObjects: []runtime.Object{},
|
||||
checluster: &chev2.CheCluster{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ExtraProperties: map[string]string{
|
||||
"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "some-test-namespace",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx := test.GetDeployContext(testCase.checluster, testCase.initObjects)
|
||||
|
||||
wp := NewWorkspacePermissionsReconciler()
|
||||
_, done, err := wp.Reconcile(ctx)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, CheWorkspacesClusterPermissionsFinalizerName))
|
||||
assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, NamespacesEditorPermissionsFinalizerName))
|
||||
assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, DevWorkspacePermissionsFinalizerName))
|
||||
|
||||
name := "eclipse-che-cheworkspaces-clusterrole"
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}))
|
||||
|
||||
name = "eclipse-che-cheworkspaces-namespaces-clusterrole"
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}))
|
||||
|
||||
name = "eclipse-che-cheworkspaces-devworkspace-clusterrole"
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const Finalizer = "cluster-resources." + constants.FinalizerSuffix
|
||||
|
||||
type Reconcilable interface {
|
||||
// Reconcile object.
|
||||
Reconcile(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error)
|
||||
|
|
@ -46,6 +48,10 @@ func (manager *ReconcileManager) RegisterReconciler(reconciler Reconcilable) {
|
|||
// Reconcile all objects in a order they have been added
|
||||
// If reconciliation failed then CheCluster status will be updated accordingly.
|
||||
func (manager *ReconcileManager) ReconcileAll(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
|
||||
if err := AppendFinalizer(ctx, Finalizer); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
for _, reconciler := range manager.reconcilers {
|
||||
result, done, err := reconciler.Reconcile(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -75,13 +81,18 @@ func (manager *ReconcileManager) FinalizeAll(ctx *chetypes.DeployContext) (done
|
|||
for _, reconciler := range manager.reconcilers {
|
||||
if completed := reconciler.Finalize(ctx); !completed {
|
||||
reconcilerName := GetObjectType(reconciler)
|
||||
errMsg := fmt.Sprintf("Finalization failed for reconciler: %s", reconcilerName)
|
||||
|
||||
ctx.CheCluster.Status.Message = errMsg
|
||||
_ = UpdateCheCRStatus(ctx, "Message", errMsg)
|
||||
ctx.CheCluster.Status.Message = fmt.Sprintf("Finalization failed for reconciler: %s", reconcilerName)
|
||||
_ = UpdateCheCRStatus(ctx, "Message", ctx.CheCluster.Status.Message)
|
||||
|
||||
done = false
|
||||
}
|
||||
}
|
||||
|
||||
if done {
|
||||
if err := CleanUpAllFinalizers(ctx); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,31 +24,43 @@ import (
|
|||
|
||||
type TestReconcilable struct {
|
||||
shouldFailReconcileOnce bool
|
||||
alreadyFailed bool
|
||||
shouldFailFinalizerOnce bool
|
||||
alreadyFailedReconcile bool
|
||||
alreadyFailedFinalizer bool
|
||||
}
|
||||
|
||||
func NewTestReconcilable(shouldFailReconcileOnce bool) *TestReconcilable {
|
||||
return &TestReconcilable{shouldFailReconcileOnce, false}
|
||||
func NewTestReconcilable(shouldFailReconcileOnce bool, shouldFailFinalizerOnce bool) *TestReconcilable {
|
||||
return &TestReconcilable{
|
||||
shouldFailReconcileOnce,
|
||||
shouldFailFinalizerOnce,
|
||||
false,
|
||||
false}
|
||||
}
|
||||
|
||||
func (tr *TestReconcilable) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
|
||||
// Fails on first invocation passes on others
|
||||
if !tr.alreadyFailed && tr.shouldFailReconcileOnce {
|
||||
tr.alreadyFailed = true
|
||||
return reconcile.Result{}, false, fmt.Errorf("Reconcile error")
|
||||
if !tr.alreadyFailedReconcile && tr.shouldFailReconcileOnce {
|
||||
tr.alreadyFailedReconcile = true
|
||||
return reconcile.Result{}, false, fmt.Errorf("reconcile error")
|
||||
} else {
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *TestReconcilable) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
return true
|
||||
// Fails on first invocation passes on others
|
||||
if !tr.alreadyFailedFinalizer && tr.shouldFailFinalizerOnce {
|
||||
tr.alreadyFailedFinalizer = true
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldUpdateAndCleanStatus(t *testing.T) {
|
||||
deployContext := test.GetDeployContext(nil, []runtime.Object{})
|
||||
|
||||
tr := NewTestReconcilable(true)
|
||||
tr := NewTestReconcilable(true, false)
|
||||
|
||||
rm := NewReconcileManager()
|
||||
rm.RegisterReconciler(tr)
|
||||
|
|
@ -58,7 +70,7 @@ func TestShouldUpdateAndCleanStatus(t *testing.T) {
|
|||
assert.False(t, done)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEmpty(t, deployContext.CheCluster.Status.Reason)
|
||||
assert.Equal(t, "Reconciler failed deploy.TestReconcilable, cause: Reconcile error", deployContext.CheCluster.Status.Message)
|
||||
assert.Equal(t, "Reconciler failed deploy.TestReconcilable, cause: reconcile error", deployContext.CheCluster.Status.Message)
|
||||
assert.Equal(t, tr, rm.failedReconciler)
|
||||
|
||||
_, done, err = rm.ReconcileAll(deployContext)
|
||||
|
|
@ -69,3 +81,39 @@ func TestShouldUpdateAndCleanStatus(t *testing.T) {
|
|||
assert.Empty(t, deployContext.CheCluster.Status.Message)
|
||||
assert.Nil(t, rm.failedReconciler)
|
||||
}
|
||||
|
||||
func TestShouldCleanUpAllFinalizers(t *testing.T) {
|
||||
ctx := test.GetDeployContext(nil, []runtime.Object{})
|
||||
|
||||
rm := NewReconcileManager()
|
||||
rm.RegisterReconciler(NewTestReconcilable(false, false))
|
||||
|
||||
_, done, err := rm.ReconcileAll(ctx)
|
||||
assert.True(t, done)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(ctx.CheCluster.Finalizers))
|
||||
|
||||
done = rm.FinalizeAll(ctx)
|
||||
assert.True(t, done)
|
||||
assert.Empty(t, ctx.CheCluster.Finalizers)
|
||||
}
|
||||
|
||||
func TestShouldNotCleanUpAllFinalizersIfFailure(t *testing.T) {
|
||||
ctx := test.GetDeployContext(nil, []runtime.Object{})
|
||||
|
||||
rm := NewReconcileManager()
|
||||
rm.RegisterReconciler(NewTestReconcilable(false, true))
|
||||
|
||||
_, done, err := rm.ReconcileAll(ctx)
|
||||
assert.True(t, done)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(ctx.CheCluster.Finalizers))
|
||||
|
||||
done = rm.FinalizeAll(ctx)
|
||||
assert.False(t, done)
|
||||
assert.Equal(t, 1, len(ctx.CheCluster.Finalizers))
|
||||
|
||||
done = rm.FinalizeAll(ctx)
|
||||
assert.True(t, done)
|
||||
assert.Empty(t, ctx.CheCluster.Finalizers)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
//
|
||||
// Copyright (c) 2019-2023 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 server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
util "github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
commonPermissionsTemplateName = "%s-cheworkspaces-clusterrole"
|
||||
namespacePermissionsTemplateName = "%s-cheworkspaces-namespaces-clusterrole"
|
||||
devWorkspacePermissionsTemplateName = "%s-cheworkspaces-devworkspace-clusterrole"
|
||||
)
|
||||
|
||||
// Create ClusterRole and ClusterRoleBinding for "che" service account.
|
||||
// che-server uses "che" service account for creation RBAC for a user in his namespace.
|
||||
func (s *CheServerReconciler) syncPermissions(ctx *chetypes.DeployContext) (bool, error) {
|
||||
policies := map[string][]rbacv1.PolicyRule{
|
||||
fmt.Sprintf(commonPermissionsTemplateName, ctx.CheCluster.Namespace): s.getCommonPolicies(),
|
||||
fmt.Sprintf(namespacePermissionsTemplateName, ctx.CheCluster.Namespace): s.getNamespaceEditorPolicies(),
|
||||
fmt.Sprintf(devWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace): s.getDevWorkspacePolicies(),
|
||||
}
|
||||
|
||||
for name, policy := range policies {
|
||||
if done, err := deploy.SyncClusterRoleToCluster(ctx, name, policy); !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, name, constants.DefaultCheServiceAccountName, name); !done {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
|
||||
cheClusterRole := strings.TrimSpace(cheClusterRole)
|
||||
if cheClusterRole != "" {
|
||||
if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, cheClusterRole, constants.DefaultCheServiceAccountName, cheClusterRole); !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
finalizer := s.getCRBFinalizerName(cheClusterRole)
|
||||
if err := deploy.AppendFinalizer(ctx, finalizer); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete abandoned CRBs
|
||||
for _, finalizer := range ctx.CheCluster.Finalizers {
|
||||
if strings.HasSuffix(finalizer, cheCRBFinalizerSuffix) {
|
||||
cheClusterRole := strings.TrimSuffix(finalizer, cheCRBFinalizerSuffix)
|
||||
if !util.Contains(ctx.CheCluster.Spec.Components.CheServer.ClusterRoles, cheClusterRole) {
|
||||
if done, err := deploy.Delete(ctx, types.NamespacedName{Name: cheClusterRole}, &rbacv1.ClusterRoleBinding{}); !done {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := deploy.DeleteFinalizer(ctx, finalizer); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) deletePermissions(ctx *chetypes.DeployContext) bool {
|
||||
names := []string{
|
||||
fmt.Sprintf(commonPermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
fmt.Sprintf(namespacePermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
fmt.Sprintf(devWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
}
|
||||
|
||||
done := true
|
||||
|
||||
for _, name := range names {
|
||||
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRole{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete ClusterRole '%s', cause: %v", name, err)
|
||||
}
|
||||
|
||||
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
|
||||
name := strings.TrimSpace(name)
|
||||
if name != "" {
|
||||
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", name, err)
|
||||
}
|
||||
|
||||
// Removes any legacy CRB https://github.com/eclipse/che/issues/19506
|
||||
legacyName := ctx.CheCluster.Namespace + "-" + constants.DefaultCheServiceAccountName + "-" + name
|
||||
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: legacyName}, &rbacv1.ClusterRoleBinding{}); err != nil {
|
||||
done = false
|
||||
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", legacyName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) getDevWorkspacePolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"workspace.devfile.io"},
|
||||
Resources: []string{"devworkspaces", "devworkspacetemplates"},
|
||||
Verbs: []string{"get", "create", "delete", "list", "update", "patch", "watch"},
|
||||
},
|
||||
}
|
||||
|
||||
return k8sPolicies
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) getNamespaceEditorPolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
Verbs: []string{"get", "create", "update", "list"},
|
||||
},
|
||||
}
|
||||
|
||||
openshiftPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projectrequests"},
|
||||
Verbs: []string{"create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projects"},
|
||||
Verbs: []string{"get", "list"},
|
||||
},
|
||||
}
|
||||
|
||||
if infrastructure.IsOpenShift() {
|
||||
return append(k8sPolicies, openshiftPolicies...)
|
||||
}
|
||||
return k8sPolicies
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) getCommonPolicies() []rbacv1.PolicyRule {
|
||||
k8sPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"serviceaccounts"},
|
||||
Verbs: []string{"get", "watch", "create"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods/exec"},
|
||||
Verbs: []string{"get", "create"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods/log"},
|
||||
Verbs: []string{"get", "list", "watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"persistentvolumeclaims"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"services"},
|
||||
Verbs: []string{"get", "list", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"configmaps"},
|
||||
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"events"},
|
||||
Verbs: []string{"watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"list"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"deployments"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"replicasets"},
|
||||
Verbs: []string{"get", "list", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"extensions"},
|
||||
Resources: []string{"ingresses"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"networking.k8s.io"},
|
||||
Resources: []string{"ingresses"},
|
||||
Verbs: []string{"get", "list", "watch", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"roles"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"rolebindings"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"metrics.k8s.io"},
|
||||
Resources: []string{"pods", "nodes"},
|
||||
Verbs: []string{"get", "list", "watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
Verbs: []string{"get", "list"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"events"},
|
||||
Verbs: []string{"watch", "list"},
|
||||
},
|
||||
}
|
||||
openshiftPolicies := []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{"route.openshift.io"},
|
||||
Resources: []string{"routes"},
|
||||
Verbs: []string{"get", "list", "create", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"authorization.openshift.io"},
|
||||
Resources: []string{"roles"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"authorization.openshift.io"},
|
||||
Resources: []string{"rolebindings"},
|
||||
Verbs: []string{"get", "create", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"project.openshift.io"},
|
||||
Resources: []string{"projects"},
|
||||
Verbs: []string{"get"},
|
||||
},
|
||||
}
|
||||
|
||||
if infrastructure.IsOpenShift() {
|
||||
return append(k8sPolicies, openshiftPolicies...)
|
||||
}
|
||||
return k8sPolicies
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) getUserClusterRoles(ctx *chetypes.DeployContext) []string {
|
||||
return []string{
|
||||
fmt.Sprintf(commonPermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
fmt.Sprintf(devWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// Copyright (c) 2019-2023 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 server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func TestSyncPermissions(t *testing.T) {
|
||||
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
checluster *chev2.CheCluster
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "Test case #1",
|
||||
checluster: &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ClusterRoles: []string{"test-role"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx := test.GetDeployContext(testCase.checluster, []runtime.Object{})
|
||||
|
||||
reconciler := NewCheServerReconciler()
|
||||
|
||||
done, err := reconciler.syncPermissions(ctx)
|
||||
assert.True(t, done)
|
||||
assert.Nil(t, err)
|
||||
|
||||
names := []string{
|
||||
fmt.Sprintf(commonPermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
fmt.Sprintf(namespacePermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
fmt.Sprintf(devWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace),
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}))
|
||||
}
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
|
||||
done = reconciler.deletePermissions(ctx)
|
||||
assert.True(t, done)
|
||||
|
||||
for _, name := range names {
|
||||
assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{}))
|
||||
assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}))
|
||||
}
|
||||
assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSyncClusterRoleBinding tests that CRB is deleted when no roles are specified in CR.
|
||||
func TestSyncPermissionsWhenCheClusterUpdated(t *testing.T) {
|
||||
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
|
||||
|
||||
cheCluster := &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ClusterRoles: []string{"test-role"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := test.GetDeployContext(cheCluster, []runtime.Object{})
|
||||
reconciler := NewCheServerReconciler()
|
||||
|
||||
done, err := reconciler.syncPermissions(ctx)
|
||||
assert.True(t, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = deploy.ReloadCheClusterCR(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
assert.Equal(t, ctx.CheCluster.Finalizers, []string{"test-role.crb.finalizers.che.eclipse.org"})
|
||||
|
||||
ctx.CheCluster.Spec.Components.CheServer.ClusterRoles = []string{}
|
||||
err = ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster)
|
||||
assert.NoError(t, err)
|
||||
|
||||
done, err = reconciler.syncPermissions(ctx)
|
||||
assert.True(t, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Namespace: "eclipse-che", Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
assert.Empty(t, ctx.CheCluster.Finalizers)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// Copyright (c) 2019-2023 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/
|
||||
|
|
@ -35,57 +35,42 @@ const (
|
|||
CheConfigMapName = "che"
|
||||
)
|
||||
|
||||
func addMap(a map[string]string, b map[string]string) {
|
||||
for k, v := range b {
|
||||
a[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
type CheConfigMap struct {
|
||||
CheHost string `json:"CHE_HOST"`
|
||||
CheMultiUser string `json:"CHE_MULTIUSER"`
|
||||
ChePort string `json:"CHE_PORT"`
|
||||
CheApi string `json:"CHE_API"`
|
||||
CheApiInternal string `json:"CHE_API_INTERNAL"`
|
||||
CheWebSocketEndpoint string `json:"CHE_WEBSOCKET_ENDPOINT"`
|
||||
CheWebSocketInternalEndpoint string `json:"CHE_WEBSOCKET_INTERNAL_ENDPOINT"`
|
||||
CheDebugServer string `json:"CHE_DEBUG_SERVER"`
|
||||
CheMetricsEnabled string `json:"CHE_METRICS_ENABLED"`
|
||||
CheInfrastructureActive string `json:"CHE_INFRASTRUCTURE_ACTIVE"`
|
||||
CheInfraKubernetesServiceAccountName string `json:"CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME"`
|
||||
CheInfraKubernetesUserClusterRoles string `json:"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"`
|
||||
DefaultTargetNamespace string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"`
|
||||
NamespaceCreationAllowed string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_CREATION__ALLOWED"`
|
||||
PvcStrategy string `json:"CHE_INFRA_KUBERNETES_PVC_STRATEGY"`
|
||||
PvcClaimSize string `json:"CHE_INFRA_KUBERNETES_PVC_QUANTITY"`
|
||||
WorkspacePvcStorageClassName string `json:"CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME"`
|
||||
TlsSupport string `json:"CHE_INFRA_OPENSHIFT_TLS__ENABLED"`
|
||||
K8STrustCerts string `json:"CHE_INFRA_KUBERNETES_TRUST__CERTS"`
|
||||
CheLogLevel string `json:"CHE_LOG_LEVEL"`
|
||||
IdentityProviderUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"`
|
||||
IdentityProviderInternalURL string `json:"CHE_OIDC_AUTH__INTERNAL__SERVER__URL,omitempty"`
|
||||
OpenShiftIdentityProvider string `json:"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"`
|
||||
JavaOpts string `json:"JAVA_OPTS"`
|
||||
WorkspaceJavaOpts string `json:"CHE_WORKSPACE_JAVA__OPTIONS"`
|
||||
WorkspaceMavenOpts string `json:"CHE_WORKSPACE_MAVEN__OPTIONS"`
|
||||
WorkspaceProxyJavaOpts string `json:"CHE_WORKSPACE_HTTP__PROXY__JAVA__OPTIONS"`
|
||||
WorkspaceHttpProxy string `json:"CHE_WORKSPACE_HTTP__PROXY"`
|
||||
WorkspaceHttpsProxy string `json:"CHE_WORKSPACE_HTTPS__PROXY"`
|
||||
WorkspaceNoProxy string `json:"CHE_WORKSPACE_NO__PROXY"`
|
||||
PluginRegistryUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__URL,omitempty"`
|
||||
PluginRegistryInternalUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL,omitempty"`
|
||||
DevfileRegistryUrl string `json:"CHE_WORKSPACE_DEVFILE__REGISTRY__URL,omitempty"`
|
||||
DevfileRegistryInternalUrl string `json:"CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL,omitempty"`
|
||||
CheWorkspacePluginBrokerMetadataImage string `json:"CHE_WORKSPACE_PLUGIN__BROKER_METADATA_IMAGE,omitempty"`
|
||||
CheWorkspacePluginBrokerArtifactsImage string `json:"CHE_WORKSPACE_PLUGIN__BROKER_ARTIFACTS_IMAGE,omitempty"`
|
||||
CheServerSecureExposerJwtProxyImage string `json:"CHE_SERVER_SECURE__EXPOSER_JWTPROXY_IMAGE,omitempty"`
|
||||
CheJGroupsKubernetesLabels string `json:"KUBERNETES_LABELS,omitempty"`
|
||||
CheTrustedCABundlesConfigMap string `json:"CHE_TRUSTED__CA__BUNDLES__CONFIGMAP,omitempty"`
|
||||
ServerStrategy string `json:"CHE_INFRA_KUBERNETES_SERVER__STRATEGY"`
|
||||
WorkspaceExposure string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_WORKSPACE_EXPOSURE"`
|
||||
SingleHostGatewayConfigMapLabels string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_GATEWAY_CONFIGMAP__LABELS"`
|
||||
CheDevWorkspacesEnabled string `json:"CHE_DEVWORKSPACES_ENABLED"`
|
||||
Http2Disable string `json:"HTTP2_DISABLE"`
|
||||
CheHost string `json:"CHE_HOST"`
|
||||
CheMultiUser string `json:"CHE_MULTIUSER"`
|
||||
ChePort string `json:"CHE_PORT"`
|
||||
CheApi string `json:"CHE_API"`
|
||||
CheApiInternal string `json:"CHE_API_INTERNAL"`
|
||||
CheWebSocketEndpoint string `json:"CHE_WEBSOCKET_ENDPOINT"`
|
||||
CheWebSocketInternalEndpoint string `json:"CHE_WEBSOCKET_INTERNAL_ENDPOINT"`
|
||||
CheDebugServer string `json:"CHE_DEBUG_SERVER"`
|
||||
CheMetricsEnabled string `json:"CHE_METRICS_ENABLED"`
|
||||
CheInfrastructureActive string `json:"CHE_INFRASTRUCTURE_ACTIVE"`
|
||||
CheInfraKubernetesServiceAccountName string `json:"CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME"`
|
||||
CheInfraKubernetesUserClusterRoles string `json:"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"`
|
||||
DefaultTargetNamespace string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"`
|
||||
NamespaceCreationAllowed string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_CREATION__ALLOWED"`
|
||||
PvcStrategy string `json:"CHE_INFRA_KUBERNETES_PVC_STRATEGY"`
|
||||
PvcClaimSize string `json:"CHE_INFRA_KUBERNETES_PVC_QUANTITY"`
|
||||
WorkspacePvcStorageClassName string `json:"CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME"`
|
||||
TlsSupport string `json:"CHE_INFRA_OPENSHIFT_TLS__ENABLED"`
|
||||
K8STrustCerts string `json:"CHE_INFRA_KUBERNETES_TRUST__CERTS"`
|
||||
CheLogLevel string `json:"CHE_LOG_LEVEL"`
|
||||
IdentityProviderUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"`
|
||||
IdentityProviderInternalURL string `json:"CHE_OIDC_AUTH__INTERNAL__SERVER__URL,omitempty"`
|
||||
OpenShiftIdentityProvider string `json:"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"`
|
||||
JavaOpts string `json:"JAVA_OPTS"`
|
||||
PluginRegistryUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__URL,omitempty"`
|
||||
PluginRegistryInternalUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL,omitempty"`
|
||||
DevfileRegistryUrl string `json:"CHE_WORKSPACE_DEVFILE__REGISTRY__URL,omitempty"`
|
||||
DevfileRegistryInternalUrl string `json:"CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL,omitempty"`
|
||||
CheJGroupsKubernetesLabels string `json:"KUBERNETES_LABELS,omitempty"`
|
||||
CheTrustedCABundlesConfigMap string `json:"CHE_TRUSTED__CA__BUNDLES__CONFIGMAP,omitempty"`
|
||||
ServerStrategy string `json:"CHE_INFRA_KUBERNETES_SERVER__STRATEGY"`
|
||||
WorkspaceExposure string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_WORKSPACE_EXPOSURE"`
|
||||
SingleHostGatewayConfigMapLabels string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_GATEWAY_CONFIGMAP__LABELS"`
|
||||
CheDevWorkspacesEnabled string `json:"CHE_DEVWORKSPACES_ENABLED"`
|
||||
Http2Disable string `json:"HTTP2_DISABLE"`
|
||||
}
|
||||
|
||||
// GetCheConfigMapData gets env values from CR spec and returns a map with key:value
|
||||
|
|
@ -187,47 +172,36 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
|
|||
webSocketInternalEndpoint := fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, ctx.CheCluster.Namespace)
|
||||
webSocketEndpoint := "wss://" + ctx.CheHost + "/api/websocket"
|
||||
cheWorkspaceServiceAccount := "NULL"
|
||||
cheUserClusterRoleNames := fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", ctx.CheCluster.Namespace, ctx.CheCluster.Namespace)
|
||||
|
||||
data := &CheConfigMap{
|
||||
CheMultiUser: "true",
|
||||
CheHost: ctx.CheHost,
|
||||
ChePort: "8080",
|
||||
CheApi: cheAPI,
|
||||
CheApiInternal: cheInternalAPI,
|
||||
CheWebSocketEndpoint: webSocketEndpoint,
|
||||
CheWebSocketInternalEndpoint: webSocketInternalEndpoint,
|
||||
CheDebugServer: cheDebug,
|
||||
CheInfrastructureActive: infra,
|
||||
CheInfraKubernetesServiceAccountName: cheWorkspaceServiceAccount,
|
||||
CheInfraKubernetesUserClusterRoles: cheUserClusterRoleNames,
|
||||
DefaultTargetNamespace: workspaceNamespaceDefault,
|
||||
NamespaceCreationAllowed: namespaceCreationAllowed,
|
||||
TlsSupport: "true",
|
||||
K8STrustCerts: "true",
|
||||
CheLogLevel: cheLogLevel,
|
||||
OpenShiftIdentityProvider: openShiftIdentityProviderId,
|
||||
JavaOpts: constants.DefaultJavaOpts + " " + proxyJavaOpts,
|
||||
WorkspaceJavaOpts: constants.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts,
|
||||
WorkspaceMavenOpts: constants.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts,
|
||||
WorkspaceProxyJavaOpts: proxyJavaOpts,
|
||||
WorkspaceHttpProxy: ctx.Proxy.HttpProxy,
|
||||
WorkspaceHttpsProxy: ctx.Proxy.HttpsProxy,
|
||||
WorkspaceNoProxy: cheWorkspaceNoProxy,
|
||||
PluginRegistryUrl: pluginRegistryURL,
|
||||
PluginRegistryInternalUrl: pluginRegistryInternalURL,
|
||||
DevfileRegistryUrl: devfileRegistryURL,
|
||||
DevfileRegistryInternalUrl: devfileRegistryInternalURL,
|
||||
CheWorkspacePluginBrokerMetadataImage: defaults.GetCheWorkspacePluginBrokerMetadataImage(ctx.CheCluster),
|
||||
CheWorkspacePluginBrokerArtifactsImage: defaults.GetCheWorkspacePluginBrokerArtifactsImage(ctx.CheCluster),
|
||||
CheServerSecureExposerJwtProxyImage: defaults.GetCheServerSecureExposerJwtProxyImage(ctx.CheCluster),
|
||||
CheJGroupsKubernetesLabels: cheLabels,
|
||||
CheMetricsEnabled: cheMetrics,
|
||||
CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName,
|
||||
ServerStrategy: "single-host",
|
||||
WorkspaceExposure: "gateway",
|
||||
SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels,
|
||||
CheDevWorkspacesEnabled: strconv.FormatBool(true),
|
||||
CheMultiUser: "true",
|
||||
CheHost: ctx.CheHost,
|
||||
ChePort: "8080",
|
||||
CheApi: cheAPI,
|
||||
CheApiInternal: cheInternalAPI,
|
||||
CheWebSocketEndpoint: webSocketEndpoint,
|
||||
CheWebSocketInternalEndpoint: webSocketInternalEndpoint,
|
||||
CheDebugServer: cheDebug,
|
||||
CheInfrastructureActive: infra,
|
||||
CheInfraKubernetesServiceAccountName: cheWorkspaceServiceAccount,
|
||||
DefaultTargetNamespace: workspaceNamespaceDefault,
|
||||
NamespaceCreationAllowed: namespaceCreationAllowed,
|
||||
TlsSupport: "true",
|
||||
K8STrustCerts: "true",
|
||||
CheLogLevel: cheLogLevel,
|
||||
OpenShiftIdentityProvider: openShiftIdentityProviderId,
|
||||
JavaOpts: constants.DefaultJavaOpts + " " + proxyJavaOpts,
|
||||
PluginRegistryUrl: pluginRegistryURL,
|
||||
PluginRegistryInternalUrl: pluginRegistryInternalURL,
|
||||
DevfileRegistryUrl: devfileRegistryURL,
|
||||
DevfileRegistryInternalUrl: devfileRegistryInternalURL,
|
||||
CheJGroupsKubernetesLabels: cheLabels,
|
||||
CheMetricsEnabled: cheMetrics,
|
||||
CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName,
|
||||
ServerStrategy: "single-host",
|
||||
WorkspaceExposure: "gateway",
|
||||
SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels,
|
||||
CheDevWorkspacesEnabled: strconv.FormatBool(true),
|
||||
// Disable HTTP2 protocol.
|
||||
// Fix issue with creating config maps on the cluster https://issues.redhat.com/browse/CRW-2677
|
||||
// The root cause is in the HTTP2 protocol support of the okttp3 library that is used by fabric8.kubernetes-client that is used by che-server
|
||||
|
|
@ -255,7 +229,7 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
|
|||
"CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM": "%s(.*)",
|
||||
}
|
||||
k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true"
|
||||
addMap(cheEnv, k8sCheEnv)
|
||||
utils.AddMap(cheEnv, k8sCheEnv)
|
||||
}
|
||||
|
||||
// Add TLS key and server certificate to properties since user workspaces is created in another
|
||||
|
|
@ -280,10 +254,12 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
|
|||
}
|
||||
}
|
||||
|
||||
addMap(cheEnv, ctx.CheCluster.Spec.Components.CheServer.ExtraProperties)
|
||||
utils.AddMap(cheEnv, ctx.CheCluster.Spec.Components.CheServer.ExtraProperties)
|
||||
|
||||
s.updateUserClusterRoles(ctx, cheEnv)
|
||||
|
||||
for _, oauthProvider := range []string{"bitbucket", "gitlab", "github", constants.AzureDevOpsOAuth} {
|
||||
err := updateIntegrationServerEndpoints(ctx, cheEnv, oauthProvider)
|
||||
err := s.updateIntegrationServerEndpoints(ctx, cheEnv, oauthProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -292,7 +268,7 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
|
|||
return cheEnv, nil
|
||||
}
|
||||
|
||||
func updateIntegrationServerEndpoints(ctx *chetypes.DeployContext, cheEnv map[string]string, oauthProvider string) error {
|
||||
func (s *CheServerReconciler) updateIntegrationServerEndpoints(ctx *chetypes.DeployContext, cheEnv map[string]string, oauthProvider string) error {
|
||||
secret, err := getOAuthConfig(ctx, oauthProvider)
|
||||
if secret == nil {
|
||||
return err
|
||||
|
|
@ -319,3 +295,25 @@ func GetCheConfigMapVersion(deployContext *chetypes.DeployContext) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) updateUserClusterRoles(ctx *chetypes.DeployContext, cheEnv map[string]string) {
|
||||
userClusterRoles := strings.Join(s.getUserClusterRoles(ctx), ", ")
|
||||
|
||||
for _, role := range strings.Split(cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"], ",") {
|
||||
role := strings.TrimSpace(role)
|
||||
if !strings.Contains(userClusterRoles, role) {
|
||||
userClusterRoles = userClusterRoles + ", " + role
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.CheCluster.Spec.DevEnvironments.User != nil {
|
||||
for _, role := range ctx.CheCluster.Spec.DevEnvironments.User.ClusterRoles {
|
||||
role := strings.TrimSpace(role)
|
||||
if !strings.Contains(userClusterRoles, role) {
|
||||
userClusterRoles = userClusterRoles + ", " + role
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"] = userClusterRoles
|
||||
}
|
||||
|
|
|
|||
|
|
@ -493,3 +493,81 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserClusterRoles(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
cheCluster *chev2.CheCluster
|
||||
expectedUserClusterRoles string
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "Test #1",
|
||||
cheCluster: &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
},
|
||||
expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole",
|
||||
},
|
||||
{
|
||||
name: "Test #2",
|
||||
cheCluster: &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ExtraProperties: map[string]string{
|
||||
"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES": "eclipse-che-cheworkspaces-clusterrole, test-roles-1, test-roles-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole, test-roles-1, test-roles-2",
|
||||
},
|
||||
{
|
||||
name: "Test #3",
|
||||
cheCluster: &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ExtraProperties: map[string]string{
|
||||
"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES": "eclipse-che-cheworkspaces-clusterrole, test-roles-1, test-roles-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
DevEnvironments: chev2.CheClusterDevEnvironments{
|
||||
User: &chev2.UserConfiguration{
|
||||
ClusterRoles: []string{
|
||||
"test-roles-3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole, test-roles-1, test-roles-2, test-roles-3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{})
|
||||
|
||||
reconciler := NewCheServerReconciler()
|
||||
cheEnv, err := reconciler.getCheConfigMapData(ctx)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.expectedUserClusterRoles, cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// Copyright (c) 2019-2023 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/
|
||||
|
|
@ -14,6 +14,7 @@ package server
|
|||
import (
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -22,6 +23,11 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const (
|
||||
crb = ".crb."
|
||||
cheCRBFinalizerSuffix = crb + constants.FinalizerSuffix
|
||||
)
|
||||
|
||||
type CheServerReconciler struct {
|
||||
deploy.Reconcilable
|
||||
}
|
||||
|
|
@ -43,6 +49,14 @@ func (s *CheServerReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.
|
|||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if done, err := deploy.SyncServiceAccountToCluster(ctx, constants.DefaultCheServiceAccountName); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if done, err := s.syncPermissions(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
done, err = s.syncDeployment(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{}, false, err
|
||||
|
|
@ -66,8 +80,8 @@ func (s *CheServerReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.
|
|||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
return true
|
||||
func (c *CheServerReconciler) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
return c.deletePermissions(ctx)
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) syncCheConfigMap(ctx *chetypes.DeployContext) (bool, error) {
|
||||
|
|
@ -115,6 +129,15 @@ func (s *CheServerReconciler) syncActiveChePhase(ctx *chetypes.DeployContext) (b
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) getCRBFinalizerName(crbName string) string {
|
||||
finalizer := crbName + cheCRBFinalizerSuffix
|
||||
diff := len(finalizer) - 63
|
||||
if diff > 0 {
|
||||
return finalizer[:len(finalizer)-diff]
|
||||
}
|
||||
return finalizer
|
||||
}
|
||||
|
||||
func (s *CheServerReconciler) syncDeployment(ctx *chetypes.DeployContext) (bool, error) {
|
||||
spec, err := s.getDeploymentSpec(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -133,6 +156,7 @@ func (s CheServerReconciler) syncCheVersion(ctx *chetypes.DeployContext) (bool,
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s CheServerReconciler) syncCheURL(ctx *chetypes.DeployContext) (bool, error) {
|
||||
var cheUrl = "https://" + ctx.CheHost
|
||||
if ctx.CheCluster.Status.CheURL != cheUrl {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// Copyright (c) 2019-2023 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/
|
||||
|
|
@ -15,13 +15,13 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
|
@ -37,6 +37,13 @@ func TestReconcile(t *testing.T) {
|
|||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
CheServer: chev2.CheServer{
|
||||
ClusterRoles: []string{"test-role"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := test.GetDeployContext(cheCluster, []runtime.Object{})
|
||||
|
|
@ -47,8 +54,11 @@ func TestReconcile(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Namespace: "eclipse-che", Name: "che"}, &corev1.ServiceAccount{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
|
||||
assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseInactive))
|
||||
assert.Equal(t, 1, len(ctx.CheCluster.Finalizers))
|
||||
|
||||
cheDeployment := &appsv1.Deployment{}
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: defaults.GetCheFlavor(), Namespace: "eclipse-che"}, cheDeployment)
|
||||
|
|
@ -63,8 +73,13 @@ func TestReconcile(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseActive))
|
||||
assert.NotEmpty(t, cheCluster.Status.CheVersion)
|
||||
assert.NotEmpty(t, cheCluster.Status.CheURL)
|
||||
assert.NotEmpty(t, ctx.CheCluster.Status.CheVersion)
|
||||
assert.NotEmpty(t, ctx.CheCluster.Status.CheURL)
|
||||
|
||||
done = server.Finalize(ctx)
|
||||
assert.True(t, done)
|
||||
|
||||
assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{}))
|
||||
}
|
||||
|
||||
func TestUpdateAvailabilityStatus(t *testing.T) {
|
||||
|
|
@ -103,3 +118,13 @@ func TestUpdateAvailabilityStatus(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.RollingUpdate))
|
||||
}
|
||||
|
||||
func TestGetFinalizerName(t *testing.T) {
|
||||
crbName := "0123456789012345678901234567890123456789" // 40 chars
|
||||
|
||||
reconciler := NewCheServerReconciler()
|
||||
finalizer := reconciler.getCRBFinalizerName(crbName)
|
||||
|
||||
assert.Equal(t, crbName+".crb.finalizers.che.ecl", finalizer)
|
||||
assert.True(t, len(finalizer) <= 63)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,25 +62,6 @@ func SyncWithClient(cli client.Client, deployContext *chetypes.DeployContext, bl
|
|||
return UpdateWithClient(cli, deployContext, actual.(client.Object), blueprint, diffOpts...)
|
||||
}
|
||||
|
||||
func SyncAndAddFinalizer(
|
||||
deployContext *chetypes.DeployContext,
|
||||
blueprint metav1.Object,
|
||||
diffOpts cmp.Option,
|
||||
finalizer string) (bool, error) {
|
||||
|
||||
// eclipse-che custom resource is being deleted, we shouldn't sync
|
||||
// TODO move this check before `Sync` invocation
|
||||
if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
done, err := Sync(deployContext, blueprint.(client.Object), diffOpts)
|
||||
if !done {
|
||||
return done, err
|
||||
}
|
||||
err = AppendFinalizer(deployContext, finalizer)
|
||||
return err == nil, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Gets object by key.
|
||||
// Returns true if object exists otherwise returns false.
|
||||
func Get(deployContext *chetypes.DeployContext, key client.ObjectKey, actual client.Object) (bool, error) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import (
|
|||
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
|
@ -171,28 +170,6 @@ func TestUpdate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSyncAndAddFinalizer(t *testing.T) {
|
||||
cli, deployContext := initDeployContext()
|
||||
|
||||
cli.Create(context.TODO(), deployContext.CheCluster)
|
||||
|
||||
// Sync object
|
||||
done, err := SyncAndAddFinalizer(deployContext, testObj.DeepCopy(), cmp.Options{}, "test-finalizer")
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Error syncing object: %v", err)
|
||||
}
|
||||
|
||||
actual := &corev1.Secret{}
|
||||
err = cli.Get(context.TODO(), testKey, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get object: %v", err)
|
||||
}
|
||||
|
||||
if !utils.Contains(deployContext.CheCluster.Finalizers, "test-finalizer") {
|
||||
t.Fatalf("Failed to add finalizer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldDeleteExistedObject(t *testing.T) {
|
||||
cli, deployContext := initDeployContext()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue