Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Provides support for installing on vanilla kubernetes #99

Merged
merged 8 commits into from
Jan 16, 2024
75 changes: 67 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ ifeq ($(DEBUG),true)
GOFLAGS += -gcflags="all=-N -l"
endif

.PHONY: image build compile go-generate test manifests k8s-generate install deploy bundle controller-gen kustomize setup operator app
.PHONY: image build compile go-generate test manifests k8s-generate install deploy bundle controller-gen kubectl kustomize check-admin setup operator app

#
# Function for editing kustomize parameters
Expand Down Expand Up @@ -135,7 +135,7 @@ get-version:
#=== Can only be executed as a cluster-admin
#
#---
deploy-crd:
deploy-crd: kubectl
kubectl apply -f $(INSTALL_ROOT)/crd/hawt.io_hawtios.yaml

#---
Expand All @@ -153,7 +153,7 @@ deploy-crd:
#** DEBUG: Print the resources to be applied instead of applying them [ true | false ]
#
#---
deploy: install kustomize
deploy: kubectl kustomize install
$(call set-kvars,$(INSTALL_ROOT))
ifeq ($(DEBUG), false)
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT) | kubectl apply -f -
Expand Down Expand Up @@ -274,6 +274,13 @@ bundle-index: opm yq
CSV_SKIPS=$(CSV_SKIP_RANGE) CSV_REPLACES=$(CSV_REPLACES) CHANNELS="$(CHANNELS)" \
./script/build_bundle_index.sh

#
# Checks if the cluster user has the necessary privileges to be a cluster-admin
# In this case if the user can list the CRDs then probably a cluster-admin
#
check-admin: kubectl
@output=$$(kubectl get crd 2>&1) || (echo "****" && echo "**** ERROR: Cannot continue as user is not a Cluster-Admin ****" && echo "****"; exit 1)

# find or download controller-gen
# download controller-gen if necessary
controller-gen:
Expand All @@ -284,6 +291,11 @@ else
CONTROLLER_GEN=$(shell command -v controller-gen 2> /dev/null)
endif

kubectl:
ifeq (, $(shell command -v kubectl 2> /dev/null))
$(error "No kubectl found in PATH. Please install and re-run")
endif

kustomize:
ifeq (, $(shell command -v kustomize 2> /dev/null))
go install sigs.k8s.io/kustomize/kustomize/v4@$(KUSTOMIZE_VERSION)
Expand Down Expand Up @@ -349,12 +361,14 @@ endif
#
#== Setup the installation by installing crds, roles and granting privileges for the installing user.
#
#=== Calls check-admin
#
#* PARAMETERS:
#** IMAGE: Set a custom image for the deployment
#** VERSION: Set a custom version for the deployment
#** NAMESPACE: Set the namespace for the resources
#** DEBUG: Print the resources to be applied instead of applying them [ true | false ]
setup: kustomize
setup: kubectl kustomize check-admin
#@ Must be invoked by a user with cluster-admin privileges
$(call set-kvars,$(INSTALL_ROOT)/setup)
ifeq ($(DEBUG), false)
Expand All @@ -378,7 +392,7 @@ endif
#** DEBUG: Print the resources to be applied instead of applying them [ true | false ]
#
#---
operator: kustomize
operator: kubectl kustomize
#@ Can be invoked by a user with namespace privileges (rather than a cluster-admin)
$(call set-kvars,$(INSTALL_ROOT)/operator)
ifeq ($(DEBUG), false)
Expand All @@ -387,6 +401,26 @@ else
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/operator
endif

#---
#
#@ cr
#
#== Install the app CR only
#
#* PARAMETERS:
#** NAMESPACE: Set the namespace for the resources
#** DEBUG: Print the resources to be applied instead of applying them [ true | false ]
#
#---
cr: kubectl kustomize
#@ Can be invoked by a user with namespace privileges (rather than a cluster-admin)
$(call set-kvars,$(INSTALL_ROOT)/app)
ifeq ($(DEBUG), false)
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app | kubectl apply -f -
else
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app
endif

#---
#
#@ app
Expand All @@ -402,15 +436,40 @@ endif
#** DEBUG: Print the resources to be applied instead of applying them [ true | false ]
#
#---
app: operator kustomize
app: kubectl kustomize operator
#@ Can be invoked by a user with namespace privileges (rather than a cluster-admin)
$(call set-kvars,$(INSTALL_ROOT)/app)
ifeq ($(DEBUG), false)
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app | kubectl apply -f -
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app | kubectl apply -f -
else
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app
$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/app
endif

UNINSTALLS = .uninstall-app .uninstall-operator .uninstall-setup

$(UNINSTALLS): kubectl kustomize
@$(call set-kvars,$(INSTALL_ROOT)/$(subst .uninstall-,,$@))
ifeq ($(DEBUG), false)
@$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/$(subst .uninstall-,,$@) | kubectl delete --ignore-not-found=true -f -
else
@$(KUSTOMIZE) build $(KOPTIONS) $(INSTALL_ROOT)/$(subst .uninstall-,,$@) | kubectl delete --dry-run=client -f -
endif

#---
#
#@ uninstall
#
#== Uninstalls the app CR, operator and setup resources
#
#=== Calls check-admin
#
#* PARAMETERS:
#** NAMESPACE: Set the namespace for the resources
#** DEBUG: Print the resources to be deleted instead of deleting them [ true | false ]
#
#---
uninstall: kubectl kustomize check-admin $(UNINSTALLS)

.DEFAULT_GOAL := help
.PHONY: help
help: ## Show this help screen.
Expand Down
140 changes: 140 additions & 0 deletions pkg/capabilities/capabilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package capabilities

import (
"context"
"errors"
"fmt"

configv1 "github.com/openshift/api/config/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kclient "k8s.io/client-go/kubernetes"

"github.com/Masterminds/semver"
errs "github.com/pkg/errors"
)

type ApiServerSpec struct {
Version string // Set to the version of the cluster
KubeVersion string // Set to the kubernetes version of the cluster (different to version if using OpenShift, for example)
IsOpenShift4 bool // Set to true if running on openshift 4
IsOpenShift43Plus bool // Set to true if running openshift 4.3+
ImageStreams bool // Set to true if the API Server supports imagestreams
Routes bool // Set to true if the API Server supports routes
ConsoleLink bool // Set to true if the API Server support the openshift console link API
}

type RequiredApiSpec struct {
routes string
imagestreams string
consolelinks string
}

var RequiredApi = RequiredApiSpec{
routes: "routes.route.openshift.io/v1",
imagestreams: "imagestreams.image.openshift.io/v1",
consolelinks: "consolelinks.console.openshift.io/v1",
}

func contains(a []string, x string) bool {
for _, n := range a {
if x == n {
return true
}
}
return false
}

func createResourceIndex(apiClient kclient.Interface) ([]string, error) {
_, apiResourceLists, err := apiClient.Discovery().ServerGroupsAndResources()
if err != nil {
return nil, err
}

resIndex := []string{}

for _, apiResList := range apiResourceLists {
for _, apiResource := range apiResList.APIResources {
resIndex = append(resIndex, fmt.Sprintf("%s.%s", apiResource.Name, apiResList.GroupVersion))
}
}

return resIndex, nil
}

// APICapabilities For testing the given platform's capabilities
func APICapabilities(ctx context.Context, apiClient kclient.Interface, configClient configclient.Interface) (*ApiServerSpec, error) {
if apiClient == nil {
return nil, errors.New("No api client. Cannot determine api capabilities")
}

if configClient == nil {
return nil, errors.New("No config client. Cannot determine api capabilities")
}

apiSpec := ApiServerSpec{}

info, err := apiClient.Discovery().ServerVersion()
if err != nil {
return nil, errs.Wrap(err, "Failed to discover server version")
}

apiSpec.KubeVersion = info.Major + "." + info.Minor

resIndex, err := createResourceIndex(apiClient)
if err != nil {
return nil, errs.Wrap(err, "Failed to create API Resource index")
}

apiSpec.Routes = contains(resIndex, RequiredApi.routes)
apiSpec.ImageStreams = contains(resIndex, RequiredApi.imagestreams)
apiSpec.ConsoleLink = contains(resIndex, RequiredApi.consolelinks)

apiSpec.IsOpenShift4 = false

var clusterVersion *configv1.ClusterVersion
if apiSpec.ConsoleLink {
//
// Update the kubernetes version to the Openshift Version
//
clusterVersion, err = configClient.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
if err == nil {
apiSpec.IsOpenShift4 = true
} else if !kerrors.IsNotFound(err) {
// Some other error rather than not found
// If error is not found then treat as not OpenShift
return nil, errs.Wrap(err, "Error reading cluster version")
}
}

if apiSpec.IsOpenShift4 && clusterVersion != nil {
// Let's take the latest version from the history
for _, update := range clusterVersion.Status.History {
if update.State == configv1.CompletedUpdate {
// Obtain the version from the last completed update
// Update the api spec version
var openShiftSemVer *semver.Version
openShiftSemVer, err = semver.NewVersion(update.Version)
apiSpec.Version = openShiftSemVer.String()

// Update whether this is OpenShift 4.3+
constraint43, _ := semver.NewConstraint(">= 4.3")
apiSpec.IsOpenShift43Plus = constraint43.Check(openShiftSemVer)
if err != nil {
return nil, errs.Wrap(err, fmt.Sprintf("Error parsing OpenShift cluster semantic version %s", update.Version))
}
break
}
}
} else {
// This is not OpenShift so plain kubernetes or something else
apiSpec.IsOpenShift4 = false

// Update version to kubernetes version
apiSpec.Version = apiSpec.KubeVersion
apiSpec.IsOpenShift43Plus = false
}

return &apiSpec, nil
}
Loading
Loading