Add airgap mode (#91)

* Determine if we are in airgap mode and patch the images appropriately
* Do the rest of the images in the che deployment
* Add map of extra images for airgap
* Add a script to generate extra_images.go, only add the extra images data to che configmap if we are in airgap mode
* Download the release version of che.properties
* Remove unnecessary properties
* Remove airGapMode boolean, make it so that setting either the airGapHostname/Organization will start the deployment in airgap mode

Signed-off-by: Tom George <tg82490@gmail.com>
pull/94/head
Tom George 2019-10-11 11:19:38 -05:00 committed by David Festal
parent 9d630545ec
commit 202b3e25b2
9 changed files with 260 additions and 36 deletions

View File

@ -31,6 +31,10 @@ type CheClusterSpec struct {
}
type CheClusterSpecServer struct {
// AirGapContainerRegistryHostname is the hostname to the internal registry to pull images from in the air-gapped environment
AirGapContainerRegistryHostname string `json:"airGapContainerRegistryHostname"`
// AirGapContainerRegistryOrganization is the repository name in the registry to pull images from in the air-gapped environment
AirGapContainerRegistryOrganization string `json:"airGapContainerRegistryOrganization"`
// CheImage is a server image used in Che deployment
CheImage string `json:"cheImage"`
// CheImageTag is a tag of an image used in Che deployment
@ -246,3 +250,8 @@ type CheClusterList struct {
func init() {
SchemeBuilder.Register(&CheCluster{}, &CheClusterList{})
}
func (c *CheCluster) IsAirGapMode() bool {
return c.Spec.Server.AirGapContainerRegistryHostname != "" ||
c.Spec.Server.AirGapContainerRegistryOrganization != ""
}

View File

@ -419,6 +419,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
// Create a new Postgres deployment
postgresDeployment := deploy.NewPostgresDeployment(instance, chePostgresPassword, isOpenShift, cheFlavor)
if err := r.CreateNewDeployment(instance, postgresDeployment); err != nil {
return reconcile.Result{}, err
}
@ -436,7 +437,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}
desiredImage := util.GetValue(instance.Spec.Database.PostgresImage, deploy.DefaultPostgresImage(cheFlavor))
desiredImage := util.GetValue(instance.Spec.Database.PostgresImage, deploy.DefaultPostgresImage(instance, cheFlavor))
effectiveImage := pgDeployment.Spec.Template.Spec.Containers[0].Image
desiredImagePullPolicy := util.GetValue(string(instance.Spec.Database.PostgresImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(desiredImage))
effectiveImagePullPolicy := string(pgDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
@ -613,7 +614,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
k8sclient.GetDeploymentRollingUpdateStatus("keycloak", instance.Namespace)
}
desiredImage := util.GetValue(instance.Spec.Auth.KeycloakImage, deploy.DefaultKeycloakImage(cheFlavor))
desiredImage := util.GetValue(instance.Spec.Auth.KeycloakImage, deploy.DefaultKeycloakImage(instance, cheFlavor))
effectiveImage := effectiveKeycloakDeployment.Spec.Template.Spec.Containers[0].Image
desiredImagePullPolicy := util.GetValue(string(instance.Spec.Auth.KeycloakImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(desiredImage))
effectiveImagePullPolicy := string(effectiveKeycloakDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
@ -803,7 +804,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
devfileRegistryURL = guessedDevfileRegistryURL
}
devfileRegistryImage := util.GetValue(instance.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(cheFlavor))
devfileRegistryImage := util.GetValue(instance.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(instance, cheFlavor))
result, err := addRegistryDeployment(
"devfile",
devfileRegistryImage,
@ -839,7 +840,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
pluginRegistryURL = guessedPluginRegistryURL
}
pluginRegistryImage := util.GetValue(instance.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(cheFlavor))
pluginRegistryImage := util.GetValue(instance.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(instance, cheFlavor))
result, err := addRegistryDeployment(
"plugin",
pluginRegistryImage,
@ -873,7 +874,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
// which will automatically trigger Che rolling update
cmResourceVersion := cheConfigMap.ResourceVersion
// create Che deployment
cheImageRepo := util.GetValue(instance.Spec.Server.CheImage, deploy.DefaultCheServerImageRepo(cheFlavor))
cheImageRepo := util.GetValue(instance.Spec.Server.CheImage, deploy.DefaultCheServerImageRepo(instance, cheFlavor))
cheImageTag := util.GetValue(instance.Spec.Server.CheImageTag, deploy.DefaultCheServerImageTag(cheFlavor))
cheDeploymentToCreate, err := deploy.NewCheDeployment(instance, cheImageRepo, cheImageTag, cmResourceVersion, isOpenShift)
if err != nil {

View File

@ -65,6 +65,9 @@ type CheConfigMap struct {
PluginRegistryUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__URL,omitempty"`
DevfileRegistryUrl string `json:"CHE_WORKSPACE_DEVFILE__REGISTRY__URL,omitempty"`
WebSocketEndpointMinor string `json:"CHE_WEBSOCKET_ENDPOINT__MINOR"`
CheWorkspacePluginBrokerInitImage string `json:"CHE_WORKSPACE_PLUGIN__BROKER_INIT_IMAGE,omitempty"`
CheWorkspacePluginBrokerUnifiedImage string `json:"CHE_WORKSPACE_PLUGIN__BROKER_UNIFIED_IMAGE,omitempty"`
CheServerSecureExposerJwtProxyImage string `json:"CHE_SERVER_SECURE__EXPOSER_JWTPROXY_IMAGE,omitempty"`
}
// GetConfigMapData gets env values from CR spec and returns a map with key:value
@ -207,6 +210,9 @@ func GetConfigMapData(cr *orgv1.CheCluster) (cheEnv map[string]string) {
}
addMap(cheEnv, cr.Spec.Server.CustomCheProperties)
if cr.IsAirGapMode() {
addMap(cheEnv, extraImagesConfig(cr))
}
return cheEnv
}
@ -225,3 +231,12 @@ func NewCheConfigMap(cr *orgv1.CheCluster, cheEnv map[string]string) *corev1.Con
Data: cheEnv,
}
}
func extraImagesConfig(cr *orgv1.CheCluster) map[string]string {
extraImages := map[string]string{
"CHE_WORKSPACE_PLUGIN__BROKER_INIT_IMAGE": patchDefaultImageName(cr, cheWorkspacePluginBrokerInitImage),
"CHE_WORKSPACE_PLUGIN__BROKER_UNIFIED_IMAGE": patchDefaultImageName(cr, cheWorkspacePluginBrokerUnifiedImage),
"CHE_SERVER_SECURE__EXPOSER_JWTPROXY_IMAGE": patchDefaultImageName(cr, cheServerSecureExposerJwtProxyImage),
}
return extraImages
}

View File

@ -13,7 +13,10 @@
package deploy
import (
"fmt"
"strings"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
)
const (
@ -55,17 +58,17 @@ const (
"-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 " +
"-Dsun.zip.disableMemoryMapping=true " +
"-Xms20m -Djava.security.egd=file:/dev/./urandom"
DefaultServerMemoryRequest = "512Mi"
DefaultServerMemoryLimit = "1Gi"
DefaultSecurityContextFsGroup = "1724"
DefaultSecurityContextRunAsUser = "1724"
DefaultServerMemoryRequest = "512Mi"
DefaultServerMemoryLimit = "1Gi"
DefaultSecurityContextFsGroup = "1724"
DefaultSecurityContextRunAsUser = "1724"
// This is only to correctly manage defaults during the transition
// from Upstream 7.0.0 GA to the next version
// That fixed bug https://github.com/eclipse/che/issues/13714
OldDefaultKeycloakUpstreamImageToDetect = "eclipse/che-keycloak:7.0.0"
OldDefaultPvcJobsUpstreamImageToDetect = "registry.access.redhat.com/ubi8-minimal:8.0-127"
OldDefaultPostgresUpstreamImageToDetect = "centos/postgresql-96-centos7:9.6"
OldDefaultKeycloakUpstreamImageToDetect = "eclipse/che-keycloak:7.0.0"
OldDefaultPvcJobsUpstreamImageToDetect = "registry.access.redhat.com/ubi8-minimal:8.0-127"
OldDefaultPostgresUpstreamImageToDetect = "centos/postgresql-96-centos7:9.6"
// ConsoleLink default
DefaultConsoleLinkName = "che"
@ -81,11 +84,12 @@ func DefaultCheServerImageTag(cheFlavor string) string {
return defaultCheServerImageTag
}
func DefaultCheServerImageRepo(cheFlavor string) string {
func DefaultCheServerImageRepo(cr *orgv1.CheCluster, cheFlavor string) string {
if cheFlavor == "codeready" {
return defaultCodeReadyServerImageRepo
return patchDefaultImageName(cr, defaultCodeReadyServerImageRepo)
} else {
return patchDefaultImageName(cr, defaultCheServerImageRepo)
}
return defaultCheServerImageRepo
}
func DefaultPvcJobsImage(cheFlavor string) string {
@ -95,33 +99,36 @@ func DefaultPvcJobsImage(cheFlavor string) string {
return defaultPvcJobsUpstreamImage
}
func DefaultPostgresImage(cheFlavor string) string {
func DefaultPostgresImage(cr *orgv1.CheCluster, cheFlavor string) string {
if cheFlavor == "codeready" {
return defaultPostgresImage
return patchDefaultImageName(cr, defaultPostgresImage)
} else {
return patchDefaultImageName(cr, defaultPostgresUpstreamImage)
}
return defaultPostgresUpstreamImage
}
func DefaultKeycloakImage(cheFlavor string) string {
func DefaultKeycloakImage(cr *orgv1.CheCluster, cheFlavor string) string {
if cheFlavor == "codeready" {
return defaultKeycloakImage
return patchDefaultImageName(cr, defaultKeycloakImage)
} else {
return patchDefaultImageName(cr, defaultKeycloakUpstreamImage)
}
return defaultKeycloakUpstreamImage
}
func DefaultPluginRegistryImage(cheFlavor string) string {
func DefaultPluginRegistryImage(cr *orgv1.CheCluster, cheFlavor string) string {
if cheFlavor == "codeready" {
return defaultPluginRegistryImage
return patchDefaultImageName(cr, defaultPluginRegistryImage)
} else {
return patchDefaultImageName(cr, defaultPluginRegistryUpstreamImage)
}
return defaultPluginRegistryUpstreamImage
}
func DefaultDevfileRegistryImage(cheFlavor string) string {
func DefaultDevfileRegistryImage(cr *orgv1.CheCluster, cheFlavor string) string {
if cheFlavor == "codeready" {
return defaultDevfileRegistryImage
return patchDefaultImageName(cr, defaultDevfileRegistryImage)
} else {
return patchDefaultImageName(cr, defaultDevfileRegistryUpstreamImage)
}
return defaultDevfileRegistryUpstreamImage
}
func DefaultPullPolicyFromDockerImage(dockerImage string) string {
@ -135,3 +142,60 @@ func DefaultPullPolicyFromDockerImage(dockerImage string) string {
}
return "IfNotPresent"
}
func patchDefaultImageName(cr *orgv1.CheCluster, imageName string) string {
if !cr.IsAirGapMode() {
return imageName
}
var hostname, organization string
if cr.Spec.Server.AirGapContainerRegistryHostname != "" {
hostname = cr.Spec.Server.AirGapContainerRegistryHostname
} else {
hostname = getHostnameFromImage(imageName)
}
if cr.Spec.Server.AirGapContainerRegistryOrganization != "" {
organization = cr.Spec.Server.AirGapContainerRegistryOrganization
} else {
organization = getOrganizationFromImage(imageName)
}
image := getImageNameFromFullImage(imageName)
return fmt.Sprintf("%s/%s/%s", hostname, organization, image)
}
func getImageNameFromFullImage(image string) string {
imageParts := strings.Split(image, "/")
nameAndTag := ""
switch len(imageParts) {
case 1:
nameAndTag = imageParts[0]
case 2:
nameAndTag = imageParts[1]
case 3:
nameAndTag = imageParts[2]
}
return nameAndTag
}
func getHostnameFromImage(image string) string {
imageParts := strings.Split(image, "/")
hostname := ""
switch len(imageParts) {
case 3:
hostname = imageParts[0]
default:
hostname = "docker.io"
}
return hostname
}
func getOrganizationFromImage(image string) string {
imageParts := strings.Split(image, "/")
organization := ""
switch len(imageParts) {
case 2:
organization = imageParts[0]
case 3:
organization = imageParts[1]
}
return organization
}

108
pkg/deploy/defaults_test.go Normal file
View File

@ -0,0 +1,108 @@
package deploy
import (
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"testing"
)
func TestCorrectImageName(t *testing.T) {
testCases := map[string]string{
"docker.io/eclipse/che-operator:latest": "che-operator:latest",
"eclipse/che-operator:7.1.0": "che-operator:7.1.0",
"che-operator:7.2.0": "che-operator:7.2.0",
}
for k, v := range testCases {
t.Run(k, func(*testing.T) {
actual := getImageNameFromFullImage(k)
if actual != v {
t.Errorf("Expected %s but was %s", v, actual)
}
})
}
}
func TestCorrectAirGapPatchedImage(t *testing.T) {
type testcase struct {
image string
expected string
cr *orgv1.CheCluster
}
upstream := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{},
},
}
crw := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CheFlavor: "codeready",
},
},
}
airGapUpstream := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryHostname: "bigcorp.net",
AirGapContainerRegistryOrganization: "che-images",
},
},
}
airGapCRW := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryHostname: "bigcorp.net",
AirGapContainerRegistryOrganization: "che-images",
CheFlavor: "codeready",
},
},
}
upstreamOnlyOrg := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryOrganization: "che-images",
},
},
}
upstreamOnlyHostname := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryHostname: "bigcorp.net",
},
},
}
crwOnlyOrg := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryOrganization: "che-images",
CheFlavor: "codeready",
},
},
}
crwOnlyHostname := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryHostname: "bigcorp.net",
CheFlavor: "codeready",
},
},
}
testCases := map[string]testcase{
"upstream default postgres": {image: defaultPostgresUpstreamImage, expected: defaultPostgresUpstreamImage, cr: upstream},
"airgap upstream postgres": {image: defaultPostgresUpstreamImage, expected: "bigcorp.net/che-images/postgresql-96-centos7:9.6", cr: airGapUpstream},
"upstream with only the org changed": {image: defaultPostgresUpstreamImage, expected: "docker.io/che-images/postgresql-96-centos7:9.6", cr: upstreamOnlyOrg},
"codeready plugin registry with only the org changed": {image: defaultPluginRegistryImage, expected: "registry.redhat.io/che-images/pluginregistry-rhel8:2.0", cr: crwOnlyOrg},
"CRW postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: crw},
"CRW airgap postgres": {image: defaultPostgresImage, expected: "bigcorp.net/che-images/postgresql-96-rhel7:1-47", cr: airGapCRW},
"upstream airgap with only hostname defined": {image: defaultKeycloakUpstreamImage, expected: "bigcorp.net/eclipse/che-keycloak:7.2.0", cr: upstreamOnlyHostname},
"crw airgap with only hostname defined": {image: defaultDevfileRegistryImage, expected: "bigcorp.net/codeready-workspaces/devfileregistry-rhel8:2.0", cr: crwOnlyHostname},
}
for name, tc := range testCases {
t.Run(name, func(*testing.T) {
actual := patchDefaultImageName(tc.cr, tc.image)
if actual != tc.expected {
t.Errorf("Expected %s but was %s", tc.expected, actual)
}
})
}
}

View File

@ -25,7 +25,7 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string
optionalEnv := true
keycloakName := "keycloak"
labels := GetLabels(cr, keycloakName)
keycloakImage := util.GetValue(cr.Spec.Auth.KeycloakImage, DefaultKeycloakImage(cheFlavor))
keycloakImage := util.GetValue(cr.Spec.Auth.KeycloakImage, DefaultKeycloakImage(cr, cheFlavor))
pullPolicy := corev1.PullPolicy(util.GetValue(string(cr.Spec.Auth.KeycloakImagePullPolicy), DefaultPullPolicyFromDockerImage(keycloakImage)))
trustpass := util.GeneratePasswd(12)
jbossDir := "/opt/eap"
@ -62,7 +62,7 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string
startCommand := "sed -i 's/WILDCARD/ANY/g' /opt/eap/bin/launch/keycloak-spi.sh && /opt/eap/bin/openshift-launch.sh -b 0.0.0.0"
// upstream Keycloak has a bit different mechanism of adding jks
changeConfigCommand := "echo Installing certificates into Keycloak && " +
"echo -e \"embed-server --server-config=standalone.xml --std-out=echo \n" +
"echo -e \"embed-server --server-config=standalone.xml --std-out=echo \n" +
"/subsystem=keycloak-server/spi=truststore/:add \n" +
"/subsystem=keycloak-server/spi=truststore/provider=file/:add(properties={file => " +
"\"" + jbossDir + "/openshift.jks\", password => \"" + trustpass + "\", disabled => \"false\" },enabled=true) \n" +
@ -230,9 +230,9 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string
Name: keycloakName,
Namespace: cr.Namespace,
Labels: labels,
Annotations: map[string]string {
Annotations: map[string]string{
"che.self-signed-certificate.version": cheCertSecretVersion,
"che.openshift-api-crt.version": openshiftCertSecretVersion,
"che.openshift-api-crt.version": openshiftCertSecretVersion,
},
},
Spec: appsv1.DeploymentSpec{

View File

@ -24,7 +24,7 @@ func NewPostgresDeployment(cr *orgv1.CheCluster, chePostgresPassword string, isO
chePostgresUser := util.GetValue(cr.Spec.Database.ChePostgresUser, "pgche")
chePostgresDb := util.GetValue(cr.Spec.Database.ChePostgresDb, "dbche")
postgresAdminPassword := util.GeneratePasswd(12)
postgresImage := util.GetValue(cr.Spec.Database.PostgresImage, DefaultPostgresImage(cheFlavor))
postgresImage := util.GetValue(cr.Spec.Database.PostgresImage, DefaultPostgresImage(cr, cheFlavor))
pullPolicy := corev1.PullPolicy(util.GetValue(string(cr.Spec.Database.PostgresImagePullPolicy), DefaultPullPolicyFromDockerImage(postgresImage)))
name := "postgres"
@ -124,11 +124,11 @@ func NewPostgresDeployment(cr *orgv1.CheCluster, chePostgresPassword string, isO
},
},
}
if ! isOpenshift {
if !isOpenshift {
var runAsUser int64 = 26
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext {
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: &runAsUser,
FSGroup: &runAsUser,
FSGroup: &runAsUser,
}
}
return &deployment

View File

@ -0,0 +1,8 @@
// This file is generated, and contains the latest versions of certain properties from che.properties
package deploy
const (
cheWorkspacePluginBrokerInitImage = "eclipse/che-init-plugin-broker:v0.21"
cheWorkspacePluginBrokerUnifiedImage = "eclipse/che-unified-plugin-broker:v0.21"
cheServerSecureExposerJwtProxyImage = "quay.io/eclipse/che-jwtproxy:dbd0578"
)

View File

@ -66,6 +66,25 @@ pkg/deploy/defaults.go \
> pkg/deploy/defaults.go.new
mv pkg/deploy/defaults.go.new pkg/deploy/defaults.go
wget https://raw.githubusercontent.com/eclipse/che/${RELEASE}/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties -q -O /tmp/che.properties
latestCheWorkspacePluginBrokerInitImage=$(cat /tmp/che.properties| grep "che.workspace.plugin_broker.init.image" | cut -d = -f2)
latestCheWorkspacePluginBrokerUnifiedImage=$(cat /tmp/che.properties | grep "che.workspace.plugin_broker.unified.image" | cut -d = -f2)
latestCheServerSecureExposerJwtProxyImage=$(cat /tmp/che.properties | grep "che.server.secure_exposer.jwtproxy.image" | cut -d = -f2)
cat << EOF > pkg/deploy/extra_images.go
// This file is generated, and contains the latest versions of certain properties from che.properties
package deploy
const (
cheWorkspacePluginBrokerInitImage = "${latestCheWorkspacePluginBrokerInitImage}"
cheWorkspacePluginBrokerUnifiedImage = "${latestCheWorkspacePluginBrokerUnifiedImage}"
cheServerSecureExposerJwtProxyImage = "${latestCheServerSecureExposerJwtProxyImage}"
)
EOF
gofmt -w pkg/deploy/extra_images.go
rm /tmp/che.properties
dockerImage="quay.io/eclipse/che-operator:${RELEASE}"
echo " - Building Che Operator docker image for new release ${RELEASE}"
docker build -t "quay.io/eclipse/che-operator:${RELEASE}" .