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
103 changes: 75 additions & 28 deletions internal/k8s/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
"slices"

"github.com/airbytehq/abctl/internal/common"
"github.com/airbytehq/abctl/internal/helm"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

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

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

var rules []networkingv1.IngressRule
for _, host := range hosts {
rules = append(rules, ingressRule(host))
rules = append(rules, ingressRules(chartVersion, host))
}

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

// ingressRule creates a rule for the host with proper API routing.
func ingressRule(host string) networkingv1.IngressRule {
var pathType = networkingv1.PathType("Prefix")
func ingressRules(chartVersion string, host string) networkingv1.IngressRule {
rules := ingressRulesForV1()
if helm.ChartIsV2Plus(chartVersion) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this we probably want to be checking against Airbyte versions? That's not something we currently do, or truly support.

rules = ingressRulesForV2()
}

return networkingv1.IngressRule{
Host: host,
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
// Route connector builder API to connector-builder-server
{
Path: "/api/v1/connector_builder",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
Host: host,
IngressRuleValue: rules,
}
}

func ingressRulesForV1() networkingv1.IngressRuleValue {
var pathType = networkingv1.PathType("Prefix")

return networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
// Route connector builder API to connector-builder-server
{
Path: "/api/v1/connector_builder",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
},
},
},
// Default route for everything else to webapp
{
Path: "/",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-webapp-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
},
},
// Default route for everything else to webapp
{
Path: "/",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-webapp-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
},
},
},
}
}

func ingressRulesForV2() networkingv1.IngressRuleValue {
var pathType = networkingv1.PathType("Prefix")

return networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
// Route connector builder API to connector-builder-server
{
Path: "/api/v1/connector_builder",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-connector-builder-server-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
},
},
},
// Default route for everything else to the server
{
Path: "/",
PathType: &pathType,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: fmt.Sprintf("%s-airbyte-server-svc", common.AirbyteChartRelease),
Port: networkingv1.ServiceBackendPort{
Name: "http",
},
},
},
Expand Down
135 changes: 113 additions & 22 deletions internal/k8s/ingress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,88 @@ import (

func TestIngress(t *testing.T) {
tests := []struct {
name string
hosts []string
expHosts []string
name string
chartVersion string
hosts []string
expHosts []string
}{
{
name: "nil hosts",
hosts: nil,
expHosts: []string{""},
name: "nil hosts v1",
chartVersion: "1.9.9",
hosts: nil,
expHosts: []string{""},
},
{
name: "empty hosts",
hosts: []string{},
expHosts: []string{""},
name: "empty hosts v1",
chartVersion: "1.9.9",
hosts: []string{},
expHosts: []string{""},
},
{
name: "single new host",
hosts: []string{"example.test"},
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
name: "single new host v1",
chartVersion: "1.9.9",
hosts: []string{"example.test"},
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
},
{
name: "localhost",
hosts: []string{"localhost"},
expHosts: []string{"localhost", "host.docker.internal"},
name: "localhost v1",
chartVersion: "1.9.9",
hosts: []string{"localhost"},
expHosts: []string{"localhost", "host.docker.internal"},
},
{
name: "host.docker.internal",
hosts: []string{"host.docker.internal"},
expHosts: []string{"localhost", "host.docker.internal"},
name: "host.docker.internal v1",
chartVersion: "1.9.9",
hosts: []string{"host.docker.internal"},
expHosts: []string{"localhost", "host.docker.internal"},
},
{
name: "multiple new hosts",
hosts: []string{"abc.test", "xyz.test"},
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
name: "multiple new hosts v1",
chartVersion: "1.9.9",
hosts: []string{"abc.test", "xyz.test"},
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
},
{
name: "nil hosts v2",
chartVersion: "2.0.0",
hosts: nil,
expHosts: []string{""},
},
{
name: "empty hosts v2",
chartVersion: "2.0.0",
hosts: []string{},
expHosts: []string{""},
},
{
name: "single new host v2",
chartVersion: "2.0.0",
hosts: []string{"example.test"},
expHosts: []string{"example.test", "localhost", "host.docker.internal"},
},
{
name: "localhost v2",
chartVersion: "2.0.0",
hosts: []string{"localhost"},
expHosts: []string{"localhost", "host.docker.internal"},
},
{
name: "host.docker.internal v2",
chartVersion: "2.0.0",
hosts: []string{"host.docker.internal"},
expHosts: []string{"localhost", "host.docker.internal"},
},
{
name: "multiple new hosts v2",
chartVersion: "2.0.0",
hosts: []string{"abc.test", "xyz.test"},
expHosts: []string{"abc.test", "localhost", "host.docker.internal", "xyz.test"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actHosts := extractHosts(Ingress(tt.hosts))
actHosts := extractHosts(Ingress(tt.chartVersion, tt.hosts))
sort.Strings(actHosts)
sort.Strings(tt.expHosts)
if d := cmp.Diff(tt.expHosts, actHosts); d != "" {
Expand All @@ -66,3 +109,51 @@ func extractHosts(ingress *networkingv1.Ingress) []string {
}
return hosts
}

func TestIngressRouting(t *testing.T) {
tests := []struct {
name string
chartVersion string
wantService string
}{
{
name: "v1 routes to webapp",
chartVersion: "1.9.9",
wantService: "airbyte-abctl-airbyte-webapp-svc",
},
{
name: "v2 routes to server",
chartVersion: "2.0.0",
wantService: "airbyte-abctl-airbyte-server-svc",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ingress := Ingress(tt.chartVersion, []string{"localhost"})

// Get the default route (path: "/")
var defaultRoute *networkingv1.HTTPIngressPath
for _, rule := range ingress.Spec.Rules {
for _, path := range rule.HTTP.Paths {
if path.Path == "/" {
defaultRoute = &path
break
}
}
if defaultRoute != nil {
break
}
}

if defaultRoute == nil {
t.Fatal("default route (/) not found")
}

gotService := defaultRoute.Backend.Service.Name
if gotService != tt.wantService {
t.Errorf("wrong service for default route: got %s, want %s", gotService, tt.wantService)
}
})
}
}
8 changes: 4 additions & 4 deletions internal/service/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (m *Manager) Install(ctx context.Context, opts *InstallOpts) error {
return fmt.Errorf("unable to install nginx chart: %w", err)
}

if err := m.handleIngress(ctx, opts.Hosts); err != nil {
if err := m.handleIngress(ctx, opts.HelmChartVersion, opts.Hosts); err != nil {
return err
}
watchStop()
Expand Down Expand Up @@ -384,14 +384,14 @@ func (m *Manager) diagnoseAirbyteChartFailure(ctx context.Context, chartErr erro
return chartErr
}

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

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

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