Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ jobs:
echo "troubleshoot version: $version"
sed -i "/^TROUBLESHOOT_VERSION/c\TROUBLESHOOT_VERSION = $version" versions.mk

- name: Helm
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
version=$(gh release list --repo helm/helm --json tagName,isLatest | jq -r '.[] | select(.isLatest) | .tagName')
echo "helm version: $version"
sed -i "/^HELM_VERSION/c\HELM_VERSION = $version" versions.mk

- name: FIO
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ output/bins/k0s-override:
chmod +x $@
touch $@

.PHONY: cmd/installer/goods/bins/helm
cmd/installer/goods/bins/helm:
$(MAKE) output/bins/helm-$(HELM_VERSION)-$(ARCH)
cp output/bins/helm-$(HELM_VERSION)-$(ARCH) $@
touch $@

output/bins/helm-%:
mkdir -p output/bins
mkdir -p output/tmp
curl --retry 5 --retry-all-errors -fL -o output/tmp/helm.tar.gz \
https://get.helm.sh/helm-$(call split-hyphen,$*,1)-$(OS)-$(call split-hyphen,$*,2).tar.gz
tar -xzf output/tmp/helm.tar.gz -C output/tmp
mv output/tmp/$(OS)-$(call split-hyphen,$*,2)/helm $@
rm -rf output/tmp
chmod +x $@
touch $@

.PHONY: cmd/installer/goods/bins/kubectl-support_bundle
cmd/installer/goods/bins/kubectl-support_bundle:
$(MAKE) output/bins/kubectl-support_bundle-$(TROUBLESHOOT_VERSION)-$(ARCH)
Expand Down Expand Up @@ -217,6 +234,7 @@ static: cmd/installer/goods/bins/k0s \
cmd/installer/goods/bins/kubectl-support_bundle \
cmd/installer/goods/bins/local-artifact-mirror \
cmd/installer/goods/bins/fio \
cmd/installer/goods/bins/helm \
cmd/installer/goods/internal/bins/kubectl-kots

.PHONY: static-dryrun
Expand All @@ -226,6 +244,7 @@ static-dryrun:
cmd/installer/goods/bins/kubectl-support_bundle \
cmd/installer/goods/bins/local-artifact-mirror \
cmd/installer/goods/bins/fio \
cmd/installer/goods/bins/helm \
cmd/installer/goods/internal/bins/kubectl-kots

.PHONY: embedded-cluster-linux-amd64
Expand Down
17 changes: 17 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
linuxupgrade "github.com/replicatedhq/embedded-cluster/api/controllers/linux/upgrade"
"github.com/replicatedhq/embedded-cluster/api/pkg/logger"
"github.com/replicatedhq/embedded-cluster/api/types"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -39,6 +40,7 @@ import (
type API struct {
cfg types.APIConfig

hcli helm.Client
logger logrus.FieldLogger
metricsReporter metrics.ReporterInterface

Expand Down Expand Up @@ -111,8 +113,19 @@ func WithMetricsReporter(metricsReporter metrics.ReporterInterface) Option {
}
}

// WithHelmClient configures the helm client for the API.
func WithHelmClient(hcli helm.Client) Option {
return func(a *API) {
a.hcli = hcli
}
}

// New creates a new API instance.
func New(cfg types.APIConfig, opts ...Option) (*API, error) {
if cfg.InstallTarget == "" {
return nil, fmt.Errorf("target is required")
}

api := &API{
cfg: cfg,
}
Expand All @@ -133,6 +146,10 @@ func New(cfg types.APIConfig, opts ...Option) (*API, error) {
api.logger = l
}

if err := api.initClients(); err != nil {
return nil, fmt.Errorf("init clients: %w", err)
}

if err := api.initHandlers(); err != nil {
return nil, fmt.Errorf("init handlers: %w", err)
}
Expand Down
88 changes: 88 additions & 0 deletions api/clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package api

import (
"fmt"

"github.com/replicatedhq/embedded-cluster/api/internal/clients"
"github.com/replicatedhq/embedded-cluster/api/types"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/versions"
)

func (a *API) initClients() error {
if a.hcli == nil {
if err := a.initHelmClient(); err != nil {
return fmt.Errorf("init helm client: %w", err)
}
}
return nil
}

// initHelmClient initializes the Helm client based on the installation target
func (a *API) initHelmClient() error {
switch a.cfg.InstallTarget {
case types.InstallTargetLinux:
return a.initLinuxHelmClient()
case types.InstallTargetKubernetes:
return a.initKubernetesHelmClient()
default:
return fmt.Errorf("unsupported install target: %s", a.cfg.InstallTarget)
}
}

// initLinuxHelmClient initializes the Helm client for Linux installations
func (a *API) initLinuxHelmClient() error {
airgapPath := ""
if a.cfg.AirgapBundle != "" {
airgapPath = a.cfg.RuntimeConfig.EmbeddedClusterChartsSubDir()
}

hcli, err := helm.NewClient(helm.HelmOptions{
HelmPath: a.cfg.RuntimeConfig.PathToEmbeddedClusterBinary("helm"),
KubernetesEnvSettings: a.cfg.RuntimeConfig.GetKubernetesEnvSettings(),
K8sVersion: versions.K0sVersion,
AirgapPath: airgapPath,
})
if err != nil {
return fmt.Errorf("create linux helm client: %w", err)
}

a.hcli = hcli
return nil
}

// initKubernetesHelmClient initializes the Helm client for Kubernetes installations
func (a *API) initKubernetesHelmClient() error {
// get the kubernetes version
kcli, err := clients.NewDiscoveryClient(clients.KubeClientOptions{
RESTClientGetter: a.cfg.Installation.GetKubernetesEnvSettings().RESTClientGetter(),
})
if err != nil {
return fmt.Errorf("create discovery client: %w", err)
}
k8sVersion, err := kcli.ServerVersion()
if err != nil {
return fmt.Errorf("get server version: %w", err)
}

// get the helm binary path
helmPath, err := a.cfg.Installation.PathToEmbeddedBinary("helm")
if err != nil {
return fmt.Errorf("get helm path: %w", err)
}

// create the helm client
hcli, err := helm.NewClient(helm.HelmOptions{
HelmPath: helmPath,
KubernetesEnvSettings: a.cfg.Installation.GetKubernetesEnvSettings(),
// TODO: how can we support airgap?
AirgapPath: "",
K8sVersion: k8sVersion.String(),
})
if err != nil {
return fmt.Errorf("create kubernetes helm client: %w", err)
}

a.hcli = hcli
return nil
}
9 changes: 9 additions & 0 deletions api/controllers/app/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/replicatedhq/embedded-cluster/api/internal/store"
"github.com/replicatedhq/embedded-cluster/api/pkg/logger"
"github.com/replicatedhq/embedded-cluster/api/types"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/release"
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -46,6 +47,7 @@ type AppController struct {
logger logrus.FieldLogger
license []byte
releaseData *release.ReleaseData
hcli helm.Client
store store.Store
configValues types.AppConfigValues
clusterID string
Expand Down Expand Up @@ -109,6 +111,12 @@ func WithReleaseData(releaseData *release.ReleaseData) AppControllerOption {
}
}

func WithHelmClient(hcli helm.Client) AppControllerOption {
return func(c *AppController) {
c.hcli = hcli
}
}

func WithConfigValues(configValues types.AppConfigValues) AppControllerOption {
return func(c *AppController) {
c.configValues = configValues
Expand Down Expand Up @@ -202,6 +210,7 @@ func NewAppController(opts ...AppControllerOption) (*AppController, error) {
appreleasemanager.WithLicense(license),
appreleasemanager.WithIsAirgap(controller.airgapBundle != ""),
appreleasemanager.WithPrivateCACertConfigMapName(controller.privateCACertConfigMapName),
appreleasemanager.WithHelmClient(controller.hcli),
)
if err != nil {
return nil, fmt.Errorf("create app release manager: %w", err)
Expand Down
53 changes: 31 additions & 22 deletions api/controllers/kubernetes/install/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
"github.com/replicatedhq/embedded-cluster/api/types"
ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
"github.com/replicatedhq/embedded-cluster/pkg-new/kubernetesinstallation"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
"github.com/replicatedhq/embedded-cluster/pkg/release"
"github.com/sirupsen/logrus"
helmcli "helm.sh/helm/v3/pkg/cli"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

type Controller interface {
Expand All @@ -34,21 +34,22 @@ type Controller interface {
var _ Controller = (*InstallController)(nil)

type InstallController struct {
installationManager installation.InstallationManager
infraManager infra.InfraManager
metricsReporter metrics.ReporterInterface
restClientGetter genericclioptions.RESTClientGetter
releaseData *release.ReleaseData
password string
tlsConfig types.TLSConfig
license []byte
airgapBundle string
configValues types.AppConfigValues
endUserConfig *ecv1beta1.Config
store store.Store
ki kubernetesinstallation.Installation
stateMachine statemachine.Interface
logger logrus.FieldLogger
installationManager installation.InstallationManager
infraManager infra.InfraManager
metricsReporter metrics.ReporterInterface
kubernetesEnvSettings *helmcli.EnvSettings
hcli helm.Client
releaseData *release.ReleaseData
password string
tlsConfig types.TLSConfig
license []byte
airgapBundle string
configValues types.AppConfigValues
endUserConfig *ecv1beta1.Config
store store.Store
ki kubernetesinstallation.Installation
stateMachine statemachine.Interface
logger logrus.FieldLogger
// App controller composition
*appcontroller.AppController
}
Expand All @@ -73,9 +74,15 @@ func WithMetricsReporter(metricsReporter metrics.ReporterInterface) InstallContr
}
}

func WithRESTClientGetter(restClientGetter genericclioptions.RESTClientGetter) InstallControllerOption {
func WithHelmClient(hcli helm.Client) InstallControllerOption {
return func(c *InstallController) {
c.restClientGetter = restClientGetter
c.hcli = hcli
}
}

func WithKubernetesEnvSettings(envSettings *helmcli.EnvSettings) InstallControllerOption {
return func(c *InstallController) {
c.kubernetesEnvSettings = envSettings
}
}

Expand Down Expand Up @@ -169,9 +176,9 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
controller.stateMachine = NewStateMachine(WithStateMachineLogger(controller.logger))
}

// If none is provided, use the default env settings from helm to create a RESTClientGetter
if controller.restClientGetter == nil {
controller.restClientGetter = helmcli.New().RESTClientGetter()
// If none is provided, use the default env settings from helm
if controller.kubernetesEnvSettings == nil {
controller.kubernetesEnvSettings = helmcli.New()
}

if controller.installationManager == nil {
Expand All @@ -192,6 +199,7 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
appcontroller.WithConfigValues(controller.configValues),
appcontroller.WithAirgapBundle(controller.airgapBundle),
appcontroller.WithPrivateCACertConfigMapName(""), // Private CA ConfigMap functionality not yet implemented for Kubernetes installations
appcontroller.WithHelmClient(controller.hcli),
)
if err != nil {
return nil, fmt.Errorf("create app install controller: %w", err)
Expand All @@ -203,13 +211,14 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
infraManager, err := infra.NewInfraManager(
infra.WithLogger(controller.logger),
infra.WithInfraStore(controller.store.KubernetesInfraStore()),
infra.WithRESTClientGetter(controller.restClientGetter),
infra.WithKubernetesEnvSettings(controller.kubernetesEnvSettings),
infra.WithPassword(controller.password),
infra.WithTLSConfig(controller.tlsConfig),
infra.WithLicense(controller.license),
infra.WithAirgapBundle(controller.airgapBundle),
infra.WithReleaseData(controller.releaseData),
infra.WithEndUserConfig(controller.endUserConfig),
infra.WithHelmClient(controller.hcli),
)
if err != nil {
return nil, fmt.Errorf("create infra manager: %w", err)
Expand Down
7 changes: 7 additions & 0 deletions api/controllers/kubernetes/install/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/replicatedhq/embedded-cluster/api/types"
ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
"github.com/replicatedhq/embedded-cluster/pkg-new/kubernetesinstallation"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
"github.com/replicatedhq/embedded-cluster/pkg/release"
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
Expand Down Expand Up @@ -111,6 +112,7 @@ func TestGetInstallationConfig(t *testing.T) {
WithInstallation(ki),
WithInstallationManager(mockManager),
WithReleaseData(getTestReleaseData(&kotsv1beta1.Config{})),
WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand Down Expand Up @@ -228,6 +230,7 @@ func TestConfigureInstallation(t *testing.T) {
WithStore(mockStore),
WithMetricsReporter(mockMetricsReporter),
WithReleaseData(getTestReleaseData(&kotsv1beta1.Config{})),
WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand Down Expand Up @@ -299,6 +302,7 @@ func TestGetInstallationStatus(t *testing.T) {
controller, err := NewInstallController(
WithInstallationManager(mockManager),
WithReleaseData(getTestReleaseData(&kotsv1beta1.Config{})),
WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand Down Expand Up @@ -425,6 +429,7 @@ func TestSetupInfra(t *testing.T) {
appcontroller.WithStore(mockStore),
appcontroller.WithReleaseData(getTestReleaseData(&appConfig)),
appcontroller.WithAppConfigManager(mockAppConfigManager),
appcontroller.WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand All @@ -437,6 +442,7 @@ func TestSetupInfra(t *testing.T) {
WithMetricsReporter(mockMetricsReporter),
WithReleaseData(getTestReleaseData(&appConfig)),
WithStore(mockStore),
WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand Down Expand Up @@ -538,6 +544,7 @@ func TestGetInfra(t *testing.T) {
controller, err := NewInstallController(
WithInfraManager(mockManager),
WithReleaseData(getTestReleaseData(&kotsv1beta1.Config{})),
WithHelmClient(&helm.MockClient{}),
)
require.NoError(t, err)

Expand Down
Loading
Loading