Skip to content

Commit b55a4f7

Browse files
authored
feat(v3): headless install orchestrator implementation (#3173)
* feat(v3): headless install orchestrator implementation * f * f * f * feedback * feedback * feedback * f * f * f * f * f * f * f * f * f * f
1 parent 2dcc2ef commit b55a4f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2716
-506
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ hack/release
3030
# Test binary, built with `go test -c`
3131
*.test
3232

33-
# helm charts dependencies
34-
*.tgz
35-
3633
# test coverage files
3734
cover.out
3835

api/api.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
1616
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
1717
"github.com/sirupsen/logrus"
18+
"k8s.io/client-go/metadata"
19+
"sigs.k8s.io/controller-runtime/pkg/client"
1820
)
1921

2022
// API represents the main HTTP API server for the Embedded Cluster application.
@@ -41,6 +43,8 @@ type API struct {
4143
cfg types.APIConfig
4244

4345
hcli helm.Client
46+
kcli client.Client
47+
mcli metadata.Interface
4448
logger logrus.FieldLogger
4549
metricsReporter metrics.ReporterInterface
4650

@@ -120,6 +124,20 @@ func WithHelmClient(hcli helm.Client) Option {
120124
}
121125
}
122126

127+
// WithKubeClient configures the kube client for the API.
128+
func WithKubeClient(kcli client.Client) Option {
129+
return func(a *API) {
130+
a.kcli = kcli
131+
}
132+
}
133+
134+
// WithMetadataClient configures the metadata client for the API.
135+
func WithMetadataClient(mcli metadata.Interface) Option {
136+
return func(a *API) {
137+
a.mcli = mcli
138+
}
139+
}
140+
123141
// New creates a new API instance.
124142
func New(cfg types.APIConfig, opts ...Option) (*API, error) {
125143
if cfg.InstallTarget == "" {

api/client/client.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ type Client interface {
1515
GetLinuxInstallationConfig(ctx context.Context) (types.LinuxInstallationConfigResponse, error)
1616
GetLinuxInstallationStatus(ctx context.Context) (types.Status, error)
1717
ConfigureLinuxInstallation(ctx context.Context, config types.LinuxInstallationConfig) (types.Status, error)
18+
RunLinuxInstallHostPreflights(ctx context.Context) (types.InstallHostPreflightsStatusResponse, error)
19+
GetLinuxInstallHostPreflightsStatus(ctx context.Context) (types.InstallHostPreflightsStatusResponse, error)
1820
SetupLinuxInfra(ctx context.Context, ignoreHostPreflights bool) (types.Infra, error)
1921
GetLinuxInfraStatus(ctx context.Context) (types.Infra, error)
22+
ProcessLinuxAirgap(ctx context.Context) (types.Airgap, error)
23+
GetLinuxAirgapStatus(ctx context.Context) (types.Airgap, error)
2024
GetLinuxInstallAppConfigValues(ctx context.Context) (types.AppConfigValues, error)
2125
PatchLinuxInstallAppConfigValues(ctx context.Context, values types.AppConfigValues) (types.AppConfigValues, error)
2226
TemplateLinuxInstallAppConfig(ctx context.Context, values types.AppConfigValues) (types.AppConfig, error)
2327
RunLinuxInstallAppPreflights(ctx context.Context) (types.InstallAppPreflightsStatusResponse, error)
2428
GetLinuxInstallAppPreflightsStatus(ctx context.Context) (types.InstallAppPreflightsStatusResponse, error)
25-
InstallLinuxApp(ctx context.Context) (types.AppInstall, error)
29+
InstallLinuxApp(ctx context.Context, ignoreAppPreflights bool) (types.AppInstall, error)
2630
GetLinuxAppInstallStatus(ctx context.Context) (types.AppInstall, error)
2731
GetLinuxUpgradeAppConfigValues(ctx context.Context) (types.AppConfigValues, error)
2832
PatchLinuxUpgradeAppConfigValues(ctx context.Context, values types.AppConfigValues) (types.AppConfigValues, error)

api/client/client_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,13 @@ func TestClient_InstallLinuxApp(t *testing.T) {
12261226
assert.Equal(t, "/api/linux/install/app/install", r.URL.Path)
12271227
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
12281228

1229+
// Decode request body
1230+
var config types.InstallAppRequest
1231+
err := json.NewDecoder(r.Body).Decode(&config)
1232+
require.NoError(t, err, "Failed to decode request body")
1233+
1234+
assert.True(t, config.IgnoreAppPreflights)
1235+
12291236
appInstall := types.AppInstall{
12301237
Status: types.Status{State: types.StateRunning, Description: "Installing app"},
12311238
Logs: "Installation started\n",
@@ -1236,7 +1243,7 @@ func TestClient_InstallLinuxApp(t *testing.T) {
12361243
defer server.Close()
12371244

12381245
c := New(server.URL, WithToken("test-token"))
1239-
appInstall, err := c.InstallLinuxApp(context.Background())
1246+
appInstall, err := c.InstallLinuxApp(context.Background(), true)
12401247

12411248
require.NoError(t, err)
12421249
assert.Equal(t, types.StateRunning, appInstall.Status.State)
@@ -1331,7 +1338,7 @@ func TestClient_AppInstallErrorHandling(t *testing.T) {
13311338
c := New(server.URL, WithToken("test-token"))
13321339

13331340
t.Run("InstallLinuxApp error", func(t *testing.T) {
1334-
_, err := c.InstallLinuxApp(context.Background())
1341+
_, err := c.InstallLinuxApp(context.Background(), true)
13351342
require.Error(t, err)
13361343
apiErr, ok := err.(*types.APIError)
13371344
require.True(t, ok)
@@ -1382,7 +1389,7 @@ func TestClient_AppInstallWithoutToken(t *testing.T) {
13821389
c := New(server.URL) // No token provided
13831390

13841391
t.Run("InstallLinuxApp without token", func(t *testing.T) {
1385-
_, err := c.InstallLinuxApp(context.Background())
1392+
_, err := c.InstallLinuxApp(context.Background(), true)
13861393
require.Error(t, err)
13871394
apiErr, ok := err.(*types.APIError)
13881395
require.True(t, ok)

api/client/install.go

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,67 @@ func (c *client) GetLinuxInstallationStatus(ctx context.Context) (types.Status,
9595
return status, nil
9696
}
9797

98+
func (c *client) RunLinuxInstallHostPreflights(ctx context.Context) (types.InstallHostPreflightsStatusResponse, error) {
99+
b, err := json.Marshal(types.PostInstallRunHostPreflightsRequest{
100+
IsUI: false,
101+
})
102+
if err != nil {
103+
return types.InstallHostPreflightsStatusResponse{}, err
104+
}
105+
106+
req, err := http.NewRequestWithContext(ctx, "POST", c.apiURL+"/api/linux/install/host-preflights/run", bytes.NewBuffer(b))
107+
if err != nil {
108+
return types.InstallHostPreflightsStatusResponse{}, err
109+
}
110+
req.Header.Set("Content-Type", "application/json")
111+
setAuthorizationHeader(req, c.token)
112+
113+
resp, err := c.httpClient.Do(req)
114+
if err != nil {
115+
return types.InstallHostPreflightsStatusResponse{}, err
116+
}
117+
defer resp.Body.Close()
118+
119+
if resp.StatusCode != http.StatusOK {
120+
return types.InstallHostPreflightsStatusResponse{}, errorFromResponse(resp)
121+
}
122+
123+
var status types.InstallHostPreflightsStatusResponse
124+
err = json.NewDecoder(resp.Body).Decode(&status)
125+
if err != nil {
126+
return types.InstallHostPreflightsStatusResponse{}, err
127+
}
128+
129+
return status, nil
130+
}
131+
132+
func (c *client) GetLinuxInstallHostPreflightsStatus(ctx context.Context) (types.InstallHostPreflightsStatusResponse, error) {
133+
req, err := http.NewRequestWithContext(ctx, "GET", c.apiURL+"/api/linux/install/host-preflights/status", nil)
134+
if err != nil {
135+
return types.InstallHostPreflightsStatusResponse{}, err
136+
}
137+
req.Header.Set("Content-Type", "application/json")
138+
setAuthorizationHeader(req, c.token)
139+
140+
resp, err := c.httpClient.Do(req)
141+
if err != nil {
142+
return types.InstallHostPreflightsStatusResponse{}, err
143+
}
144+
defer resp.Body.Close()
145+
146+
if resp.StatusCode != http.StatusOK {
147+
return types.InstallHostPreflightsStatusResponse{}, errorFromResponse(resp)
148+
}
149+
150+
var status types.InstallHostPreflightsStatusResponse
151+
err = json.NewDecoder(resp.Body).Decode(&status)
152+
if err != nil {
153+
return types.InstallHostPreflightsStatusResponse{}, err
154+
}
155+
156+
return status, nil
157+
}
158+
98159
func (c *client) SetupLinuxInfra(ctx context.Context, ignoreHostPreflights bool) (types.Infra, error) {
99160
b, err := json.Marshal(types.LinuxInfraSetupRequest{
100161
IgnoreHostPreflights: ignoreHostPreflights,
@@ -156,6 +217,60 @@ func (c *client) GetLinuxInfraStatus(ctx context.Context) (types.Infra, error) {
156217
return infra, nil
157218
}
158219

220+
func (c *client) ProcessLinuxAirgap(ctx context.Context) (types.Airgap, error) {
221+
req, err := http.NewRequestWithContext(ctx, "POST", c.apiURL+"/api/linux/install/airgap/process", nil)
222+
if err != nil {
223+
return types.Airgap{}, err
224+
}
225+
req.Header.Set("Content-Type", "application/json")
226+
setAuthorizationHeader(req, c.token)
227+
228+
resp, err := c.httpClient.Do(req)
229+
if err != nil {
230+
return types.Airgap{}, err
231+
}
232+
defer resp.Body.Close()
233+
234+
if resp.StatusCode != http.StatusOK {
235+
return types.Airgap{}, errorFromResponse(resp)
236+
}
237+
238+
var airgap types.Airgap
239+
err = json.NewDecoder(resp.Body).Decode(&airgap)
240+
if err != nil {
241+
return types.Airgap{}, err
242+
}
243+
244+
return airgap, nil
245+
}
246+
247+
func (c *client) GetLinuxAirgapStatus(ctx context.Context) (types.Airgap, error) {
248+
req, err := http.NewRequestWithContext(ctx, "GET", c.apiURL+"/api/linux/install/airgap/status", nil)
249+
if err != nil {
250+
return types.Airgap{}, err
251+
}
252+
req.Header.Set("Content-Type", "application/json")
253+
setAuthorizationHeader(req, c.token)
254+
255+
resp, err := c.httpClient.Do(req)
256+
if err != nil {
257+
return types.Airgap{}, err
258+
}
259+
defer resp.Body.Close()
260+
261+
if resp.StatusCode != http.StatusOK {
262+
return types.Airgap{}, errorFromResponse(resp)
263+
}
264+
265+
var airgap types.Airgap
266+
err = json.NewDecoder(resp.Body).Decode(&airgap)
267+
if err != nil {
268+
return types.Airgap{}, err
269+
}
270+
271+
return airgap, nil
272+
}
273+
159274
func (c *client) GetKubernetesInstallationConfig(ctx context.Context) (types.KubernetesInstallationConfigResponse, error) {
160275
req, err := http.NewRequestWithContext(ctx, "GET", c.apiURL+"/api/kubernetes/install/installation/config", nil)
161276
if err != nil {
@@ -601,8 +716,16 @@ func (c *client) GetKubernetesInstallAppPreflightsStatus(ctx context.Context) (t
601716
return status, nil
602717
}
603718

604-
func (c *client) InstallLinuxApp(ctx context.Context) (types.AppInstall, error) {
605-
req, err := http.NewRequestWithContext(ctx, "POST", c.apiURL+"/api/linux/install/app/install", nil)
719+
func (c *client) InstallLinuxApp(ctx context.Context, ignoreAppPreflights bool) (types.AppInstall, error) {
720+
request := types.InstallAppRequest{
721+
IgnoreAppPreflights: ignoreAppPreflights,
722+
}
723+
b, err := json.Marshal(request)
724+
if err != nil {
725+
return types.AppInstall{}, err
726+
}
727+
728+
req, err := http.NewRequestWithContext(ctx, "POST", c.apiURL+"/api/linux/install/app/install", bytes.NewBuffer(b))
606729
if err != nil {
607730
return types.AppInstall{}, err
608731
}

api/controllers/app/apppreflight.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflig
5353
return fmt.Errorf("extract app preflight spec: %w", err)
5454
}
5555
if appPreflightSpec == nil {
56+
// TODO: support for installing without an app preflight spec
5657
return fmt.Errorf("no app preflight spec found")
5758
}
5859

api/controllers/kubernetes/install/controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/replicatedhq/embedded-cluster/pkg/release"
2020
"github.com/sirupsen/logrus"
2121
helmcli "helm.sh/helm/v3/pkg/cli"
22+
"k8s.io/client-go/metadata"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
2324
)
2425

@@ -40,6 +41,7 @@ type InstallController struct {
4041
metricsReporter metrics.ReporterInterface
4142
hcli helm.Client
4243
kcli client.Client
44+
mcli metadata.Interface
4345
kubernetesEnvSettings *helmcli.EnvSettings
4446
releaseData *release.ReleaseData
4547
password string
@@ -88,6 +90,12 @@ func WithKubeClient(kcli client.Client) InstallControllerOption {
8890
}
8991
}
9092

93+
func WithMetadataClient(mcli metadata.Interface) InstallControllerOption {
94+
return func(c *InstallController) {
95+
c.mcli = mcli
96+
}
97+
}
98+
9199
func WithKubernetesEnvSettings(envSettings *helmcli.EnvSettings) InstallControllerOption {
92100
return func(c *InstallController) {
93101
c.kubernetesEnvSettings = envSettings
@@ -229,6 +237,8 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
229237
infra.WithReleaseData(controller.releaseData),
230238
infra.WithEndUserConfig(controller.endUserConfig),
231239
infra.WithHelmClient(controller.hcli),
240+
infra.WithKubeClient(controller.kcli),
241+
infra.WithMetadataClient(controller.mcli),
232242
)
233243
if err != nil {
234244
return nil, fmt.Errorf("create infra manager: %w", err)

api/controllers/linux/install/controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/replicatedhq/embedded-cluster/pkg/release"
2525
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
2626
"github.com/sirupsen/logrus"
27+
"k8s.io/client-go/metadata"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
2829
)
2930

@@ -72,6 +73,7 @@ type InstallController struct {
7273
rc runtimeconfig.RuntimeConfig
7374
hcli helm.Client
7475
kcli client.Client
76+
mcli metadata.Interface
7577
stateMachine statemachine.Interface
7678
logger logrus.FieldLogger
7779
allowIgnoreHostPreflights bool
@@ -231,6 +233,12 @@ func WithKubeClient(kcli client.Client) InstallControllerOption {
231233
}
232234
}
233235

236+
func WithMetadataClient(mcli metadata.Interface) InstallControllerOption {
237+
return func(c *InstallController) {
238+
c.mcli = mcli
239+
}
240+
}
241+
234242
func NewInstallController(opts ...InstallControllerOption) (*InstallController, error) {
235243
controller := &InstallController{
236244
store: store.NewMemoryStore(),
@@ -319,6 +327,8 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
319327
infra.WithEndUserConfig(controller.endUserConfig),
320328
infra.WithClusterID(controller.clusterID),
321329
infra.WithHelmClient(controller.hcli),
330+
infra.WithKubeClient(controller.kcli),
331+
infra.WithMetadataClient(controller.mcli),
322332
)
323333
}
324334

api/controllers/linux/upgrade/controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/replicatedhq/embedded-cluster/pkg/release"
2424
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
2525
"github.com/sirupsen/logrus"
26+
"k8s.io/client-go/metadata"
2627
"sigs.k8s.io/controller-runtime/pkg/client"
2728
)
2829

@@ -58,6 +59,7 @@ type UpgradeController struct {
5859
rc runtimeconfig.RuntimeConfig
5960
hcli helm.Client
6061
kcli client.Client
62+
mcli metadata.Interface
6163
stateMachine statemachine.Interface
6264
requiresInfraUpgrade bool
6365
logger logrus.FieldLogger
@@ -189,6 +191,12 @@ func WithKubeClient(kcli client.Client) UpgradeControllerOption {
189191
}
190192
}
191193

194+
func WithMetadataClient(mcli metadata.Interface) UpgradeControllerOption {
195+
return func(c *UpgradeController) {
196+
c.mcli = mcli
197+
}
198+
}
199+
192200
func WithEndUserConfig(endUserConfig *ecv1beta1.Config) UpgradeControllerOption {
193201
return func(c *UpgradeController) {
194202
c.endUserConfig = endUserConfig
@@ -262,6 +270,8 @@ func NewUpgradeController(opts ...UpgradeControllerOption) (*UpgradeController,
262270
infra.WithEndUserConfig(controller.endUserConfig),
263271
infra.WithClusterID(controller.clusterID),
264272
infra.WithHelmClient(controller.hcli),
273+
infra.WithKubeClient(controller.kcli),
274+
infra.WithMetadataClient(controller.mcli),
265275
)
266276
}
267277

api/handlers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ func (a *API) initHandlers() error {
5959
linuxhandler.WithInstallController(a.linuxInstallController),
6060
linuxhandler.WithUpgradeController(a.linuxUpgradeController),
6161
linuxhandler.WithHelmClient(a.hcli),
62+
linuxhandler.WithKubeClient(a.kcli),
63+
linuxhandler.WithMetadataClient(a.mcli),
6264
)
6365
if err != nil {
6466
return fmt.Errorf("new linux handler: %w", err)
@@ -72,6 +74,8 @@ func (a *API) initHandlers() error {
7274
kuberneteshandler.WithInstallController(a.kubernetesInstallController),
7375
kuberneteshandler.WithUpgradeController(a.kubernetesUpgradeController),
7476
kuberneteshandler.WithHelmClient(a.hcli),
77+
kuberneteshandler.WithKubeClient(a.kcli),
78+
kuberneteshandler.WithMetadataClient(a.mcli),
7579
)
7680
if err != nil {
7781
return fmt.Errorf("new kubernetes handler: %w", err)

0 commit comments

Comments
 (0)