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
27 changes: 23 additions & 4 deletions internal/cmd/install/dataplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import (

// DataplaneCmd handles dataplane installation
type DataplaneCmd struct {
Namespace string `short:"n" help:"Target namespace (default: 'default' for Kind clusters, current kubeconfig context for existing clusters)."`
Namespace string `short:"n" help:"Target namespace (default: 'default' for Kind clusters, current kubeconfig context for existing clusters)."`
ChartVersion string `help:"Version of the airbyte-data-plane chart to install (default: latest chart V2 release)."`
// Hide flag for now.
WithKindCluster bool `default:"true" help:"Create a Kind cluster for the dataplane and set it as current context." hidden:""`
}
Expand Down Expand Up @@ -62,6 +63,12 @@ func (c *DataplaneCmd) Run(
return err
}

chartURL, chartVersion, repoURL, err := helm.ResolveDataplaneChartReference(c.ChartVersion)
if err != nil {
return fmt.Errorf("failed to resolve dataplane chart: %w", err)
}
c.warnOnChartV1(ui)

// Setup Kind cluster if needed
var kubeconfig, kubeContext, clusterName string
if c.WithKindCluster {
Expand All @@ -84,7 +91,7 @@ func (c *DataplaneCmd) Run(
}

// Deploy to Kubernetes
if err := deployChart(ctx, ui, helmClient, c.Namespace, name, creds, abContext); err != nil {
if err := deployChart(ctx, ui, helmClient, c.Namespace, name, chartURL, chartVersion, repoURL, creds, abContext); err != nil {
return err
}

Expand All @@ -93,6 +100,18 @@ func (c *DataplaneCmd) Run(
return nil
}

// warnOnChartV1 notifies the user when an explicitly requested chart version
// comes from the retired V1 Helm repository.
func (c *DataplaneCmd) warnOnChartV1(ui ui.Provider) {
if c.ChartVersion == "" || helm.ChartIsV2PlusBaseVersion(c.ChartVersion) {
return
}
ui.ShowInfo(fmt.Sprintf(
"Warning: chart V1 (%s) is retired and no longer receives updates. Migrate to chart V2: https://docs.airbyte.com/platform/deploying-airbyte/chart-v2-community",
c.ChartVersion,
Comment thread
dgplatform26 marked this conversation as resolved.
))
}

// resolveNamespace sets the namespace based on cluster type
func (c *DataplaneCmd) resolveNamespace() error {
if c.Namespace != "" {
Expand Down Expand Up @@ -176,9 +195,9 @@ func registerDataplane(ctx context.Context, ui ui.Provider, apiClient api.Servic
}

// deployChart installs the Helm chart
func deployChart(ctx context.Context, ui ui.Provider, client goHelm.Client, namespace, name string, creds *api.CreateDataplaneResponse, context *airbox.Context) error {
func deployChart(ctx context.Context, ui ui.Provider, client goHelm.Client, namespace, name, chartURL, chartVersion, repoURL string, creds *api.CreateDataplaneResponse, context *airbox.Context) error {
return ui.RunWithSpinner("Installing dataplane chart", func() error {
return helm.InstallDataplaneChart(ctx, client, namespace, name, creds, context)
return helm.InstallDataplaneChart(ctx, client, namespace, name, chartURL, chartVersion, repoURL, creds, context)
})
}

Expand Down
40 changes: 38 additions & 2 deletions internal/cmd/install/dataplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestDataplaneCmd_Run(t *testing.T) {
name string
namespace string
withKind bool
chartVersion string
expectedError string
setupMocks func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, helm.Factory, k8s.ClusterFactory, *uimock.MockProvider)
}{
Expand Down Expand Up @@ -241,8 +242,9 @@ func TestDataplaneCmd_Run(t *testing.T) {
},
},
{
name: "success flow with Kind cluster",
withKind: true,
name: "success flow with Kind cluster",
withKind: true,
chartVersion: "1.9.2",
setupMocks: func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, helm.Factory, k8s.ClusterFactory, *uimock.MockProvider) {
mockStore := airboxmock.NewMockConfigStore(ctrl)
mockHTTP := httpmock.NewMockHTTPDoer(ctrl)
Expand Down Expand Up @@ -291,6 +293,7 @@ func TestDataplaneCmd_Run(t *testing.T) {
}, nil),
mockUI.EXPECT().FilterableSelect("Select existing region:", []string{"Test Region - US (AWS)"}).Return(0, "Test Region - US (AWS)", nil),
mockUI.EXPECT().TextInput("Enter dataplane name:", "my-dataplane", gomock.Any()).Return("test-dataplane", nil),
mockUI.EXPECT().ShowInfo(gomock.Any()),
mockUI.EXPECT().RunWithSpinner("Creating Kind cluster", gomock.Any()).DoAndReturn(func(msg string, fn func() error) error {
return fn()
}),
Expand Down Expand Up @@ -407,8 +410,14 @@ func TestDataplaneCmd_Run(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

chartVersion := tt.chartVersion
if chartVersion == "" {
chartVersion = "2.1.1"
}

cmd := &DataplaneCmd{
Namespace: tt.namespace,
ChartVersion: chartVersion,
WithKindCluster: tt.withKind,
}

Expand All @@ -425,3 +434,30 @@ func TestDataplaneCmd_Run(t *testing.T) {
})
}
}

func TestDataplaneCmdWarnOnChartV1(t *testing.T) {
tests := []struct {
name string
chartVersion string
warn bool
}{
{name: "v1 chart", chartVersion: "1.9.2", warn: true},
{name: "v2 prerelease chart", chartVersion: "2.0.0-rc1", warn: false},
{name: "empty chart version", chartVersion: "", warn: false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ui := uimock.NewMockProvider(ctrl)
if tt.warn {
ui.EXPECT().ShowInfo(gomock.Any())
}

cmd := &DataplaneCmd{ChartVersion: tt.chartVersion}
cmd.warnOnChartV1(ui)
})
}
}
5 changes: 5 additions & 0 deletions internal/cmd/local/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ func (i *InstallCmd) setDefaultChartFlags(helmClient goHelm.Client) error {

i.Chart = resolvedChart
i.ChartVersion = resolvedVersion
if resolvedVersion != "" && !helm.ChartIsV2PlusBaseVersion(resolvedVersion) {
pterm.Warning.Printfln(
"Chart V1 is deprecated. Upgrade to the V2 chart: https://docs.airbyte.com/platform/deploying-airbyte/chart-v2-community",
)
}

return nil
}
13 changes: 9 additions & 4 deletions internal/helm/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ func ChartIsV2Plus(v string) bool {
return ChartEqualsOrHigherVersion(v, "v2.0.0")
}

// ChartIsV2PlusBaseVersion returns true if the base version before any
// pre-release suffix is v2.0.0 or higher.
func ChartIsV2PlusBaseVersion(v string) bool {
baseVersion, _, _ := strings.Cut(v, "-")
return ChartIsV2Plus(baseVersion)
}

// ChartIsV1Dot8Plus returns true if the chart version is v1.8.0 or higher
func ChartIsV1Dot8Plus(v string) bool {
return ChartEqualsOrHigherVersion(v, "v1.8.0")
Expand Down Expand Up @@ -72,10 +79,8 @@ func (r *ChartResolver) ResolveChartReference(chart, version string) (string, st
}
return chartURL, chartVersion, nil
} else {
// Extract base version without suffix (e.g., "1.8.4-rc5" -> "1.8.4")
// to determine which repo to use, but keep full version for URL
baseVersion, _, _ := strings.Cut(version, "-")
if ChartIsV2Plus(baseVersion) {
// Extract the base version to determine which repo to use, but keep the full version for URL.
if ChartIsV2PlusBaseVersion(version) {
// Construct the v2 chart URL.
return fmt.Sprintf("%s/airbyte-%s.tgz", r.v2RepoURL, version), version, nil
} else {
Expand Down
41 changes: 41 additions & 0 deletions internal/helm/chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ func TestChartIsV2Plus(t *testing.T) {
}
}

func TestChartIsV2PlusBaseVersion(t *testing.T) {
tests := []struct {
name string
ver string
want bool
}{
{name: "empty version", ver: "", want: false},
{name: "v1 version", ver: "1.9.2", want: false},
{name: "v2 release candidate", ver: "2.0.0-rc1", want: true},
{name: "v2 version", ver: "2.0.0", want: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, ChartIsV2PlusBaseVersion(tt.ver))
})
}
}

func TestChartIsV1Dot8Plus(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -314,3 +333,25 @@ entries:
})
}
}

func TestGetLatestChartURLFromRepoIndex(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `apiVersion: v1
entries:
airbyte-data-plane:
- name: "airbyte-data-plane"
version: "2.1.1"
urls: ["airbyte-data-plane-2.1.1.tgz"]
- name: "airbyte-data-plane"
version: "2.1.2-beta.1"
urls: ["airbyte-data-plane-2.1.2-beta.1.tgz"]
`)
}))
defer server.Close()

url, version, err := GetLatestChartURLFromRepoIndex("airbyte", server.URL, "airbyte-data-plane")

assert.NoError(t, err)
assert.Equal(t, server.URL+"/airbyte-data-plane-2.1.1.tgz", url)
assert.Equal(t, "2.1.1", version)
}
41 changes: 32 additions & 9 deletions internal/helm/dataplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,41 @@ import (
)

const (
dataplaneChartName = "airbyte/airbyte-data-plane"
dataplaneChartVersion = "1.8.1"
dataplaneRepoName = "airbyte"
dataplaneRepoURL = common.AirbyteRepoURLv1 // v1 repo has the dataplane chart
dataplaneRepoName = "airbyte"
dataplaneChartName = "airbyte-data-plane"
)

// InstallDataplaneChart installs the official Airbyte dataplane Helm chart
func InstallDataplaneChart(ctx context.Context, client goHelm.Client, namespace, releaseName string, credentials *api.CreateDataplaneResponse, config *airbox.Context) error {
var getLatestDataplaneChartURL = func(repoURL string) (string, string, error) {
return GetLatestChartURLFromRepoIndex(dataplaneRepoName, repoURL, dataplaneChartName)
}

// ResolveDataplaneChartReference resolves the dataplane chart URL, version and
// source repository for the requested version. An empty version resolves the
// latest stable chart from the v2 repository; a version below 2.0.0 resolves
// against the deprecated v1 repository.
func ResolveDataplaneChartReference(version string) (chartURL, chartVersion, repoURL string, err error) {
if version == "" {
chartURL, chartVersion, err = getLatestDataplaneChartURL(common.AirbyteRepoURLv2)
if err != nil {
return "", "", "", fmt.Errorf("failed to resolve latest dataplane chart: %w", err)
}
return chartURL, chartVersion, common.AirbyteRepoURLv2, nil
}

repoURL = common.AirbyteRepoURLv1
if ChartIsV2PlusBaseVersion(version) {
repoURL = common.AirbyteRepoURLv2
}

return fmt.Sprintf("%s/%s-%s.tgz", repoURL, dataplaneChartName, version), version, repoURL, nil
}

// InstallDataplaneChart installs the official Airbyte dataplane Helm chart.
func InstallDataplaneChart(ctx context.Context, client goHelm.Client, namespace, releaseName, chartURL, chartVersion, repoURL string, credentials *api.CreateDataplaneResponse, config *airbox.Context) error {
// Add the Airbyte Helm repository
if err := client.AddOrUpdateChartRepo(repo.Entry{
Name: dataplaneRepoName,
URL: dataplaneRepoURL,
URL: repoURL,
}); err != nil {
return fmt.Errorf("failed to add Airbyte chart repository: %w", err)
}
Expand All @@ -35,8 +58,8 @@ func InstallDataplaneChart(ctx context.Context, client goHelm.Client, namespace,
// Install the chart with atomic flag to ensure cleanup on failure/interrupt
_, err := client.InstallOrUpgradeChart(ctx, &goHelm.ChartSpec{
ReleaseName: releaseName,
ChartName: dataplaneChartName,
Version: dataplaneChartVersion,
ChartName: chartURL,
Version: chartVersion,
CreateNamespace: true,
Namespace: namespace,
Wait: true,
Expand Down
Loading
Loading