Skip to content
Open
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
70 changes: 67 additions & 3 deletions cli/internal/aks/deploy/cilium.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package deploy
import (
"context"
"errors"
"fmt"
"log"
"net/url"
"os"
"os/exec"

"github.com/Azure/aks-flex/plugin/pkg/util/config"
"k8s.io/client-go/tools/clientcmd"
)

var ciliumInstallInstruction = errors.New(
Expand All @@ -28,18 +32,78 @@ func deployCilium(
kubeconfigFile string,
cfg *config.Config,
) error {
k8sServiceHost, k8sServicePort, err := kubeconfigAPIServer(kubeconfigFile)
if err != nil {
return err
}

cmd := exec.CommandContext(
ctx,
"cilium", "install",
"--set", "azure.resourceGroup="+cfg.ResourceGroupName,
"--kubeconfig", kubeconfigFile,
"--context", cfg.ClusterName+"-admin",
"--namespace", "kube-system",
"--datapath-mode", "aks-byocni",
"--helm-set", "aksbyocni.enabled=true",
"--helm-set", "cluster.name="+cfg.ClusterName,
"--helm-set", "operator.replicas=1",
"--helm-set", "kubeProxyReplacement=true",
"--helm-set", "k8sServiceHost="+k8sServiceHost,
"--helm-set", "k8sServicePort="+k8sServicePort,
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(
cmd.Env,
cmd.Environ(),
"KUBECONFIG="+kubeconfigFile,
"PATH="+os.Getenv("PATH"), // so cilium can find other tools
"PATH="+os.Getenv("PATH"),
)
log.Printf("Running: cilium install --kubeconfig %s --context %s --namespace kube-system --datapath-mode aks-byocni --helm-set aksbyocni.enabled=true --helm-set cluster.name=%s --helm-set operator.replicas=1 --helm-set kubeProxyReplacement=true --helm-set k8sServiceHost=%s --helm-set k8sServicePort=%s", kubeconfigFile, cfg.ClusterName+"-admin", cfg.ClusterName, k8sServiceHost, k8sServicePort)

return cmd.Run()
}

func kubeconfigAPIServer(kubeconfigFile string) (string, string, error) {
kcfg, err := clientcmd.LoadFromFile(kubeconfigFile)
if err != nil {
return "", "", fmt.Errorf("loading kubeconfig for cilium install: %w", err)
}

ctxName := kcfg.CurrentContext
if ctxName == "" {
return "", "", errors.New("kubeconfig missing current context")
}

ctxCfg, ok := kcfg.Contexts[ctxName]
if !ok || ctxCfg == nil {
return "", "", fmt.Errorf("kubeconfig missing context %q", ctxName)
}

clusterCfg, ok := kcfg.Clusters[ctxCfg.Cluster]
if !ok || clusterCfg == nil {
return "", "", fmt.Errorf("kubeconfig missing cluster %q", ctxCfg.Cluster)
}

u, err := url.Parse(clusterCfg.Server)
if err != nil {
return "", "", fmt.Errorf("parsing API server URL %q: %w", clusterCfg.Server, err)
}

hostname := u.Hostname()
port := u.Port()
if hostname == "" {
return "", "", fmt.Errorf("API server URL missing hostname: %q", clusterCfg.Server)
}
if port == "" {
switch u.Scheme {
case "https":
port = "443"
case "http":
port = "80"
default:
return "", "", fmt.Errorf("API server URL missing port and unsupported scheme %q", u.Scheme)
}
}

return hostname, port, nil
}
42 changes: 42 additions & 0 deletions cli/internal/aks/deploy/cilium_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package deploy

import (
"os"
"path/filepath"
"testing"
)

func TestKubeconfigAPIServer(t *testing.T) {
dir := t.TempDir()
kubeconfig := filepath.Join(dir, "config")
if err := os.WriteFile(kubeconfig, []byte(`apiVersion: v1
kind: Config
current-context: test
clusters:
- name: test-cluster
cluster:
server: https://example.hcp.eastus2.azmk8s.io:443
contexts:
- name: test
context:
cluster: test-cluster
user: test-user
users:
- name: test-user
user:
token: test
`), 0o600); err != nil {
t.Fatalf("write kubeconfig: %v", err)
}

host, port, err := kubeconfigAPIServer(kubeconfig)
if err != nil {
t.Fatalf("kubeconfigAPIServer returned error: %v", err)
}
if host != "example.hcp.eastus2.azmk8s.io" {
t.Fatalf("unexpected host %q", host)
}
if port != "443" {
t.Fatalf("unexpected port %q", port)
}
}
42 changes: 41 additions & 1 deletion cli/internal/config/configcmd/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package configcmd

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
Expand Down Expand Up @@ -42,7 +44,11 @@ func OrPlaceholder(val string) string {
// reachable or the required environment variables are not set, it falls back
// to placeholder values that the user must replace manually.
func DefaultKubeadmConfig(ctx context.Context) *kubeadm.Config {
credentials, err := azidentity.NewAzureCLICredential(nil)
credOptions := &azidentity.AzureCLICredentialOptions{}
if tenantID := azureConfigTenantID(); tenantID != "" {
credOptions.TenantID = tenantID
}
credentials, err := azidentity.NewAzureCLICredential(credOptions)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: could not obtain Azure CLI credentials: %v\n", err)
fmt.Fprintln(os.Stderr, "Using placeholder values — edit the output before applying.")
Expand All @@ -64,3 +70,37 @@ func DefaultKubeadmConfig(ctx context.Context) *kubeadm.Config {
}
return cfg
}

func azureConfigTenantID() string {
azureConfigDir := os.Getenv("AZURE_CONFIG_DIR")
if azureConfigDir == "" {
azureConfigDir = filepath.Join(os.Getenv("HOME"), ".azure")
}

b, err := os.ReadFile(filepath.Join(azureConfigDir, "azureProfile.json"))
if err != nil {
return ""
}

var profile struct {
Subscriptions []struct {
IsDefault bool `json:"isDefault"`
TenantID string `json:"tenantId"`
} `json:"subscriptions"`
}
if err := json.Unmarshal(b, &profile); err != nil {
return ""
}

for _, sub := range profile.Subscriptions {
if sub.IsDefault && sub.TenantID != "" {
return sub.TenantID
}
}

if len(profile.Subscriptions) == 1 {
return profile.Subscriptions[0].TenantID
}

return ""
}
24 changes: 24 additions & 0 deletions cli/internal/config/configcmd/defaults_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package configcmd

import (
"os"
"path/filepath"
"testing"
)

func TestAzureConfigTenantIDUsesAzureConfigDirProfile(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
azureConfigDir := filepath.Join(t.TempDir(), "azure-profile")
if err := os.MkdirAll(azureConfigDir, 0o755); err != nil {
t.Fatalf("mkdir azure config dir: %v", err)
}
if err := os.WriteFile(filepath.Join(azureConfigDir, "azureProfile.json"), []byte(`{"subscriptions":[{"id":"sub","isDefault":true,"tenantId":"tenant-123"}]}`), 0o600); err != nil {
t.Fatalf("write azureProfile.json: %v", err)
}
t.Setenv("AZURE_CONFIG_DIR", azureConfigDir)

if got := azureConfigTenantID(); got != "tenant-123" {
t.Fatalf("unexpected tenant id %q", got)
}
}
2 changes: 1 addition & 1 deletion cli/internal/config/nodebootstrap/assets/script.sh.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ apt-get update -y
apt-get upgrade -y
{{- end }}
{{- if .Packages }}
apt-get install -y {{ join .Packages " " }}
apt-get install -y --allow-change-held-packages {{ join .Packages " " }}
{{- end }}
{{- end }}
{{- if .FQDN }}
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/config/nodebootstrap/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func Test_marshalScript_basic(t *testing.T) {
"#!/bin/bash",
"set -euo pipefail",
"apt-get update -y",
"apt-get install -y curl",
"apt-get install -y --allow-change-held-packages curl",
"mkdir -p '/tmp'",
`cat <<'EOF' > '/tmp/config.json'`,
`{"key":"value"}`,
Expand Down
7 changes: 6 additions & 1 deletion plugin/pkg/util/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ func defaultSubscriptionID() string {
return subscriptionID
}

b, err := os.ReadFile(filepath.Join(os.Getenv("HOME"), ".azure/clouds.config"))
azureConfigDir := os.Getenv("AZURE_CONFIG_DIR")
if azureConfigDir == "" {
azureConfigDir = filepath.Join(os.Getenv("HOME"), ".azure")
}

b, err := os.ReadFile(filepath.Join(azureConfigDir, "clouds.config"))
if err != nil {
return ""
}
Expand Down
27 changes: 27 additions & 0 deletions plugin/pkg/util/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package config

import (
"os"
"path/filepath"
"testing"
)

func TestDefaultSubscriptionIDHonorsAzureConfigDir(t *testing.T) {
t.Setenv("AZURE_SUBSCRIPTION_ID", "")
home := t.TempDir()
t.Setenv("HOME", home)

azureConfigDir := filepath.Join(t.TempDir(), "azure-custom")
if err := os.MkdirAll(azureConfigDir, 0o755); err != nil {
t.Fatalf("mkdir azureConfigDir: %v", err)
}
if err := os.WriteFile(filepath.Join(azureConfigDir, "clouds.config"), []byte("[AzureCloud]\nsubscription = 11111111-2222-3333-4444-555555555555\n"), 0o600); err != nil {
t.Fatalf("write clouds.config: %v", err)
}
t.Setenv("AZURE_CONFIG_DIR", azureConfigDir)

got := defaultSubscriptionID()
if got != "11111111-2222-3333-4444-555555555555" {
t.Fatalf("unexpected subscription id %q", got)
}
}
Loading