Skip to content

Commit 8742b79

Browse files
authored
feat: route ingress traffic based on chart version (#175)
1 parent c140e70 commit 8742b79

File tree

3 files changed

+192
-54
lines changed

3 files changed

+192
-54
lines changed

internal/k8s/ingress.go

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"slices"
66

77
"github.com/airbytehq/abctl/internal/common"
8+
"github.com/airbytehq/abctl/internal/helm"
89
networkingv1 "k8s.io/api/networking/v1"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011
)
1112

1213
// Ingress creates an ingress type for defining the webapp ingress rules.
13-
func Ingress(hosts []string) *networkingv1.Ingress {
14+
func Ingress(chartVersion string, hosts []string) *networkingv1.Ingress {
1415
var ingressClassName = "nginx"
1516

1617
// if no host is defined, default to an empty host
@@ -31,7 +32,7 @@ func Ingress(hosts []string) *networkingv1.Ingress {
3132

3233
var rules []networkingv1.IngressRule
3334
for _, host := range hosts {
34-
rules = append(rules, ingressRule(host))
35+
rules = append(rules, ingressRules(chartVersion, host))
3536
}
3637

3738
return &networkingv1.Ingress{
@@ -48,37 +49,83 @@ func Ingress(hosts []string) *networkingv1.Ingress {
4849
}
4950

5051
// ingressRule creates a rule for the host with proper API routing.
51-
func ingressRule(host string) networkingv1.IngressRule {
52-
var pathType = networkingv1.PathType("Prefix")
52+
func ingressRules(chartVersion string, host string) networkingv1.IngressRule {
53+
rules := ingressRulesForV1()
54+
if helm.ChartIsV2Plus(chartVersion) {
55+
rules = ingressRulesForV2()
56+
}
5357

5458
return networkingv1.IngressRule{
55-
Host: host,
56-
IngressRuleValue: networkingv1.IngressRuleValue{
57-
HTTP: &networkingv1.HTTPIngressRuleValue{
58-
Paths: []networkingv1.HTTPIngressPath{
59-
// Route connector builder API to connector-builder-server
60-
{
61-
Path: "/api/v1/connector_builder",
62-
PathType: &pathType,
63-
Backend: networkingv1.IngressBackend{
64-
Service: &networkingv1.IngressServiceBackend{
65-
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
66-
Port: networkingv1.ServiceBackendPort{
67-
Name: "http",
68-
},
59+
Host: host,
60+
IngressRuleValue: rules,
61+
}
62+
}
63+
64+
func ingressRulesForV1() networkingv1.IngressRuleValue {
65+
var pathType = networkingv1.PathType("Prefix")
66+
67+
return networkingv1.IngressRuleValue{
68+
HTTP: &networkingv1.HTTPIngressRuleValue{
69+
Paths: []networkingv1.HTTPIngressPath{
70+
// Route connector builder API to connector-builder-server
71+
{
72+
Path: "/api/v1/connector_builder",
73+
PathType: &pathType,
74+
Backend: networkingv1.IngressBackend{
75+
Service: &networkingv1.IngressServiceBackend{
76+
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
77+
Port: networkingv1.ServiceBackendPort{
78+
Name: "http",
79+
},
80+
},
81+
},
82+
},
83+
// Default route for everything else to webapp
84+
{
85+
Path: "/",
86+
PathType: &pathType,
87+
Backend: networkingv1.IngressBackend{
88+
Service: &networkingv1.IngressServiceBackend{
89+
Name: fmt.Sprintf("%s-airbyte-webapp-svc", common.AirbyteChartRelease),
90+
Port: networkingv1.ServiceBackendPort{
91+
Name: "http",
6992
},
7093
},
7194
},
72-
// Default route for everything else to webapp
73-
{
74-
Path: "/",
75-
PathType: &pathType,
76-
Backend: networkingv1.IngressBackend{
77-
Service: &networkingv1.IngressServiceBackend{
78-
Name: fmt.Sprintf("%s-airbyte-webapp-svc", common.AirbyteChartRelease),
79-
Port: networkingv1.ServiceBackendPort{
80-
Name: "http",
81-
},
95+
},
96+
},
97+
},
98+
}
99+
}
100+
101+
func ingressRulesForV2() networkingv1.IngressRuleValue {
102+
var pathType = networkingv1.PathType("Prefix")
103+
104+
return networkingv1.IngressRuleValue{
105+
HTTP: &networkingv1.HTTPIngressRuleValue{
106+
Paths: []networkingv1.HTTPIngressPath{
107+
// Route connector builder API to connector-builder-server
108+
{
109+
Path: "/api/v1/connector_builder",
110+
PathType: &pathType,
111+
Backend: networkingv1.IngressBackend{
112+
Service: &networkingv1.IngressServiceBackend{
113+
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
114+
Port: networkingv1.ServiceBackendPort{
115+
Name: "http",
116+
},
117+
},
118+
},
119+
},
120+
// Default route for everything else to the server
121+
{
122+
Path: "/",
123+
PathType: &pathType,
124+
Backend: networkingv1.IngressBackend{
125+
Service: &networkingv1.IngressServiceBackend{
126+
Name: fmt.Sprintf("%s-airbyte-server-svc", common.AirbyteChartRelease),
127+
Port: networkingv1.ServiceBackendPort{
128+
Name: "http",
82129
},
83130
},
84131
},

internal/k8s/ingress_test.go

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,88 @@ import (
1010

1111
func TestIngress(t *testing.T) {
1212
tests := []struct {
13-
name string
14-
hosts []string
15-
expHosts []string
13+
name string
14+
chartVersion string
15+
hosts []string
16+
expHosts []string
1617
}{
1718
{
18-
name: "nil hosts",
19-
hosts: nil,
20-
expHosts: []string{""},
19+
name: "nil hosts v1",
20+
chartVersion: "1.9.9",
21+
hosts: nil,
22+
expHosts: []string{""},
2123
},
2224
{
23-
name: "empty hosts",
24-
hosts: []string{},
25-
expHosts: []string{""},
25+
name: "empty hosts v1",
26+
chartVersion: "1.9.9",
27+
hosts: []string{},
28+
expHosts: []string{""},
2629
},
2730
{
28-
name: "single new host",
29-
hosts: []string{"example.test"},
30-
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
31+
name: "single new host v1",
32+
chartVersion: "1.9.9",
33+
hosts: []string{"example.test"},
34+
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
3135
},
3236
{
33-
name: "localhost",
34-
hosts: []string{"localhost"},
35-
expHosts: []string{"localhost", "host.docker.internal"},
37+
name: "localhost v1",
38+
chartVersion: "1.9.9",
39+
hosts: []string{"localhost"},
40+
expHosts: []string{"localhost", "host.docker.internal"},
3641
},
3742
{
38-
name: "host.docker.internal",
39-
hosts: []string{"host.docker.internal"},
40-
expHosts: []string{"localhost", "host.docker.internal"},
43+
name: "host.docker.internal v1",
44+
chartVersion: "1.9.9",
45+
hosts: []string{"host.docker.internal"},
46+
expHosts: []string{"localhost", "host.docker.internal"},
4147
},
4248
{
43-
name: "multiple new hosts",
44-
hosts: []string{"abc.test", "xyz.test"},
45-
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
49+
name: "multiple new hosts v1",
50+
chartVersion: "1.9.9",
51+
hosts: []string{"abc.test", "xyz.test"},
52+
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
53+
},
54+
{
55+
name: "nil hosts v2",
56+
chartVersion: "2.0.0",
57+
hosts: nil,
58+
expHosts: []string{""},
59+
},
60+
{
61+
name: "empty hosts v2",
62+
chartVersion: "2.0.0",
63+
hosts: []string{},
64+
expHosts: []string{""},
65+
},
66+
{
67+
name: "single new host v2",
68+
chartVersion: "2.0.0",
69+
hosts: []string{"example.test"},
70+
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
71+
},
72+
{
73+
name: "localhost v2",
74+
chartVersion: "2.0.0",
75+
hosts: []string{"localhost"},
76+
expHosts: []string{"localhost", "host.docker.internal"},
77+
},
78+
{
79+
name: "host.docker.internal v2",
80+
chartVersion: "2.0.0",
81+
hosts: []string{"host.docker.internal"},
82+
expHosts: []string{"localhost", "host.docker.internal"},
83+
},
84+
{
85+
name: "multiple new hosts v2",
86+
chartVersion: "2.0.0",
87+
hosts: []string{"abc.test", "xyz.test"},
88+
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
4689
},
4790
}
4891

4992
for _, tt := range tests {
5093
t.Run(tt.name, func(t *testing.T) {
51-
actHosts := extractHosts(Ingress(tt.hosts))
94+
actHosts := extractHosts(Ingress(tt.chartVersion, tt.hosts))
5295
sort.Strings(actHosts)
5396
sort.Strings(tt.expHosts)
5497
if d := cmp.Diff(tt.expHosts, actHosts); d != "" {
@@ -66,3 +109,51 @@ func extractHosts(ingress *networkingv1.Ingress) []string {
66109
}
67110
return hosts
68111
}
112+
113+
func TestIngressRouting(t *testing.T) {
114+
tests := []struct {
115+
name string
116+
chartVersion string
117+
wantService string
118+
}{
119+
{
120+
name: "v1 routes to webapp",
121+
chartVersion: "1.9.9",
122+
wantService: "airbyte-abctl-airbyte-webapp-svc",
123+
},
124+
{
125+
name: "v2 routes to server",
126+
chartVersion: "2.0.0",
127+
wantService: "airbyte-abctl-airbyte-server-svc",
128+
},
129+
}
130+
131+
for _, tt := range tests {
132+
t.Run(tt.name, func(t *testing.T) {
133+
ingress := Ingress(tt.chartVersion, []string{"localhost"})
134+
135+
// Get the default route (path: "/")
136+
var defaultRoute *networkingv1.HTTPIngressPath
137+
for _, rule := range ingress.Spec.Rules {
138+
for _, path := range rule.HTTP.Paths {
139+
if path.Path == "/" {
140+
defaultRoute = &path
141+
break
142+
}
143+
}
144+
if defaultRoute != nil {
145+
break
146+
}
147+
}
148+
149+
if defaultRoute == nil {
150+
t.Fatal("default route (/) not found")
151+
}
152+
153+
gotService := defaultRoute.Backend.Service.Name
154+
if gotService != tt.wantService {
155+
t.Errorf("wrong service for default route: got %s, want %s", gotService, tt.wantService)
156+
}
157+
})
158+
}
159+
}

internal/service/install.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ func (m *Manager) Install(ctx context.Context, opts *InstallOpts) error {
308308
return fmt.Errorf("unable to install nginx chart: %w", err)
309309
}
310310

311-
if err := m.handleIngress(ctx, opts.Hosts); err != nil {
311+
if err := m.handleIngress(ctx, opts.HelmChartVersion, opts.Hosts); err != nil {
312312
return err
313313
}
314314
watchStop()
@@ -384,14 +384,14 @@ func (m *Manager) diagnoseAirbyteChartFailure(ctx context.Context, chartErr erro
384384
return chartErr
385385
}
386386

387-
func (m *Manager) handleIngress(ctx context.Context, hosts []string) error {
387+
func (m *Manager) handleIngress(ctx context.Context, chartVersion string, hosts []string) error {
388388
ctx, span := trace.NewSpan(ctx, "command.handleIngress")
389389
defer span.End()
390390
m.spinner.UpdateText("Checking for existing Ingress")
391391

392392
if m.k8s.IngressExists(ctx, common.AirbyteNamespace, common.AirbyteIngress) {
393393
pterm.Success.Println("Found existing Ingress")
394-
if err := m.k8s.IngressUpdate(ctx, common.AirbyteNamespace, k8s.Ingress(hosts)); err != nil {
394+
if err := m.k8s.IngressUpdate(ctx, common.AirbyteNamespace, k8s.Ingress(chartVersion, hosts)); err != nil {
395395
pterm.Error.Printfln("Unable to update existing Ingress")
396396
return fmt.Errorf("unable to update existing ingress: %w", err)
397397
}
@@ -400,7 +400,7 @@ func (m *Manager) handleIngress(ctx context.Context, hosts []string) error {
400400
}
401401

402402
pterm.Info.Println("No existing Ingress found, creating one")
403-
if err := m.k8s.IngressCreate(ctx, common.AirbyteNamespace, k8s.Ingress(hosts)); err != nil {
403+
if err := m.k8s.IngressCreate(ctx, common.AirbyteNamespace, k8s.Ingress(chartVersion, hosts)); err != nil {
404404
pterm.Error.Println("Unable to create ingress")
405405
return fmt.Errorf("unable to create ingress: %w", err)
406406
}

0 commit comments

Comments
 (0)