Update core-zero import client

This commit is contained in:
Joe Kralicky 2024-10-01 12:47:16 -04:00
parent 77665603ce
commit 35e4b782ea
No known key found for this signature in database
GPG key ID: 75C4875F34A9FB79
15 changed files with 122 additions and 780 deletions

View file

@ -964,6 +964,17 @@ func TestOptions_ApplySettings(t *testing.T) {
})
}
func TestXXX(t *testing.T) {
dir, _ := os.MkdirTemp("", "asdf")
t.Log(dir)
for i := 1; i <= 100; i++ {
crt, _ := cryptutil.GenerateCertificate(nil, fmt.Sprintf("route%d.localhost.pomerium.io", i))
crtBytes, keyBytes, _ := cryptutil.EncodeCertificate(crt)
os.WriteFile(fmt.Sprintf("%s/%d.crt", dir, i), crtBytes, 0o644)
os.WriteFile(fmt.Sprintf("%s/%d.key", dir, i), keyBytes, 0o600)
}
}
func TestOptions_GetSetResponseHeaders(t *testing.T) {
t.Run("lax", func(t *testing.T) {
options := NewDefaultOptions()

21
go.mod
View file

@ -17,11 +17,6 @@ require (
github.com/caddyserver/certmagic v0.21.3
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cespare/xxhash/v2 v2.3.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/lipgloss v0.13.0
github.com/charmbracelet/x/ansi v0.2.3
github.com/charmbracelet/x/exp/teatest v0.0.0-20240913162256-9ef7ff40e654
github.com/cloudflare/circl v1.4.0
github.com/coreos/go-oidc/v3 v3.11.0
github.com/docker/docker v27.2.0+incompatible
@ -48,7 +43,6 @@ require (
github.com/minio/minio-go/v7 v7.0.76
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a
github.com/natefinch/atomic v1.0.1
github.com/oapi-codegen/runtime v1.1.1
github.com/open-policy-agent/opa v0.68.0
@ -117,7 +111,6 @@ require (
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.31 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
@ -133,16 +126,9 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.6 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/charmbracelet/bubbles v0.20.0 // indirect
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.0 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@ -151,7 +137,6 @@ require (
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
@ -184,20 +169,15 @@ require (
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/miekg/dns v1.1.59 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
@ -211,7 +191,6 @@ require (
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/statsd_exporter v0.22.7 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect

46
go.sum
View file

@ -67,8 +67,6 @@ github.com/DataDog/datadog-go v3.5.0+incompatible h1:AShr9cqkF+taHjyQgcBcQUt/ZNK
github.com/DataDog/datadog-go v3.5.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/opencensus-go-exporter-datadog v0.0.0-20200406135749-5c268882acf0 h1:Y6HFfo8UuntPOpfmUmLb0o3MNYKfUuH2aNmvypsDbY4=
github.com/DataDog/opencensus-go-exporter-datadog v0.0.0-20200406135749-5c268882acf0/go.mod h1:/VV3EFO/hTNQZHAqaj+CPGy2+ioFrP4EX3iRwozubhQ=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@ -96,8 +94,6 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU=
@ -134,10 +130,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.6 h1:TrQadF7GcqvQ63kgwEcjlrVc2Fa0
github.com/aws/aws-sdk-go-v2/service/sts v1.30.6/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o=
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -151,8 +143,6 @@ github.com/caddyserver/certmagic v0.21.3 h1:pqRRry3yuB4CWBVq9+cUqu+Y6E2z8TswbhNx
github.com/caddyserver/certmagic v0.21.3/go.mod h1:Zq6pklO9nVRl3DIFUw9gVUfXKdpc/0qwTUAQMBlfgtI=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -165,22 +155,6 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY=
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/exp/teatest v0.0.0-20240913162256-9ef7ff40e654 h1:d+B9FUkxeEex8Q5p4pafFxZbUMzE/TJ64Y5bFDPKcd4=
github.com/charmbracelet/x/exp/teatest v0.0.0-20240913162256-9ef7ff40e654/go.mod h1:NDRRSMP6bZbCs4jyc4i1/4UG4M+0PEiQdpivQgD0Mio=
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -234,8 +208,6 @@ github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnv
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@ -463,16 +435,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c h1:TRkEV8M5PhQU55WI49FKTszEIpFlwZ1wfxcACCRT7SE=
github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c/go.mod h1:oJwexVSshEat0E3evyKOH6QzN8GFWrhLvEoh8GiJzss=
github.com/kralicky/huh v0.0.0-20240918162853-8fb158fa8e43 h1:0FzXYOXrusgxJNC8e71PuwtahoeLCpqEvk3EM3gHnGA=
github.com/kralicky/huh v0.0.0-20240918162853-8fb158fa8e43/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@ -486,10 +454,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/acmez/v2 v2.0.2 h1:OmK6xckte2JfKGPz4OAA8aNHTiLvGp8tLzmrd/wfSyw=
github.com/mholt/acmez/v2 v2.0.2/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
@ -514,12 +478,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -626,9 +584,6 @@ github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
@ -943,7 +898,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -3,7 +3,6 @@ package zero
import (
"bytes"
"compress/gzip"
"context"
"fmt"
"io"
@ -13,6 +12,7 @@ import (
"google.golang.org/grpc/keepalive"
"google.golang.org/protobuf/proto"
"github.com/klauspost/compress/zstd"
"github.com/pomerium/pomerium/internal/zero/apierror"
connect_mux "github.com/pomerium/pomerium/internal/zero/connect-mux"
"github.com/pomerium/pomerium/internal/zero/grpcconn"
@ -121,13 +121,16 @@ func (api *API) GetClusterResourceBundles(ctx context.Context) (*cluster_api.Get
)
}
func (api *API) ImportConfig(ctx context.Context, cfg *configpb.Config) (*cluster_api.EmptyResponse, error) {
func (api *API) ImportConfig(ctx context.Context, cfg *configpb.Config) (*cluster_api.ImportResponse, error) {
data, err := proto.Marshal(cfg)
if err != nil {
return nil, err
}
var compressedData bytes.Buffer
w := gzip.NewWriter(&compressedData)
w, err := zstd.NewWriter(&compressedData, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
if err != nil {
panic(fmt.Sprintf("bug: %v", err))
}
_, err = io.Copy(w, bytes.NewReader(data))
if err != nil {
return nil, err

View file

@ -9,6 +9,7 @@ import (
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/pkg/envoy/files"
"github.com/pomerium/pomerium/pkg/zero/importutil"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)
@ -69,21 +70,24 @@ func BuildImportCmd() *cobra.Command {
cfg := src.GetConfig()
client := zeroClientFromContext(cmd.Context())
quotas, err := client.GetQuotas(cmd.Context())
if err != nil {
return fmt.Errorf("error getting quotas: %w", err)
}
converted := cfg.Options.ToProto()
ui := NewImportUI(converted, quotas)
if err := ui.Run(cmd.Context()); err != nil {
return err
for i, name := range importutil.GenerateRouteNames(converted.Routes) {
converted.Routes[i].Name = name
}
ui.ApplySelections(converted)
_, err = client.ImportConfig(cmd.Context(), converted)
resp, err := client.ImportConfig(cmd.Context(), converted)
if err != nil {
return fmt.Errorf("error importing config: %w", err)
}
cmd.PrintErrln("config imported successfully")
if resp.Warnings != nil {
for _, warn := range *resp.Warnings {
cmd.Printf("warning: %s\n", warn)
}
}
if resp.Messages != nil {
for _, msg := range *resp.Messages {
cmd.Printf("✔ %s\n", msg)
}
}
return nil
},
}

View file

@ -1,7 +0,0 @@
package cmd
import "github.com/charmbracelet/huh"
func (ui *ImportUI) XForm() *huh.Form {
return ui.form
}

View file

@ -1,553 +0,0 @@
package cmd
import (
"bytes"
"context"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"os"
"slices"
"strconv"
"strings"
"github.com/cespare/xxhash/v2"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
http_connection_managerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
"github.com/muesli/termenv"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log"
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
"github.com/pomerium/pomerium/pkg/zero/importutil"
"github.com/pomerium/protoutil/fieldmasks"
"github.com/pomerium/protoutil/paths"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/reflect/protopath"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
type onCursorUpdate struct {
Field interface{ Hovered() (string, bool) }
}
func (u onCursorUpdate) Hash() (uint64, error) {
op, ok := u.Field.Hovered()
if !ok {
return ^uint64(0), nil
}
return xxhash.Sum64String(op), nil
}
var (
yellowText = lipgloss.NewStyle().Foreground(lipgloss.ANSIColor(3))
faintText = lipgloss.NewStyle().Faint(true).UnsetForeground()
redText = lipgloss.NewStyle().Foreground(lipgloss.ANSIColor(1))
)
func errText(err error) string {
return redText.Render(fmt.Sprintf("(error: %v)", err))
}
func certInfoFromSettingsCertificate(v protoreflect.Value) string {
switch v := v.Interface().(type) {
case protoreflect.List:
buf := bytes.Buffer{}
for i, l := 0, v.Len(); i < l; i++ {
crtBytes := string(v.Get(i).Message().Interface().(*configpb.Settings_Certificate).GetCertBytes())
buf.WriteString(crtBytes)
if i < l-1 {
buf.WriteRune('\n')
}
}
return certInfoFromBytes(buf.Bytes())
case protoreflect.Message:
crtBytes := string(v.Interface().(*configpb.Settings_Certificate).GetCertBytes())
return certInfoFromBytes([]byte(crtBytes))
default:
panic(fmt.Sprintf("bug: unexpected value type %T", v))
}
}
func certInfoFromBase64(v protoreflect.Value) string {
crtBytes, err := base64.StdEncoding.DecodeString(v.String())
if err != nil {
return errText(err)
}
return certInfoFromBytes(crtBytes)
}
func certInfoFromBytes(b []byte) string {
if len(b) == 0 {
return faintText.Render("(empty)")
}
block, rest := pem.Decode(b)
if block == nil {
return errText(errors.New("no PEM data found"))
}
extraBlocks := []*pem.Block{}
for len(rest) > 0 {
block, rest = pem.Decode(rest)
if block != nil {
extraBlocks = append(extraBlocks, block)
}
}
blockType := block.Type
var info string
switch block.Type {
case "X509 CRL":
crl, err := x509.ParseRevocationList(block.Bytes)
if err != nil {
return errText(err)
}
info = fmt.Sprintf("%d entries", len(crl.RevokedCertificateEntries))
default:
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return errText(err)
}
info = *importutil.GenerateCertName(cert)
}
out := yellowText.Render(fmt.Sprintf("(%s: %s)", blockType, info))
if len(extraBlocks) > 0 {
s := ""
if len(extraBlocks) != 1 {
s = "s"
}
out += faintText.Render(fmt.Sprintf(" ...+%d block%s", len(extraBlocks), s))
}
return out
}
func secret(s protoreflect.Value) string {
length := len(s.String())
return yellowText.Render(fmt.Sprintf("(secret: %d bytes)", length))
}
var customSettingsInfoByPath = map[string]func(v protoreflect.Value) string{
"(pomerium.config.Settings).metrics_certificate": certInfoFromSettingsCertificate,
"(pomerium.config.Settings).metrics_client_ca": certInfoFromBase64,
"(pomerium.config.Settings).certificates": certInfoFromSettingsCertificate,
"(pomerium.config.Settings).certificate_authority": certInfoFromBase64,
"(pomerium.config.Settings).downstream_mtls.ca": certInfoFromBase64,
"(pomerium.config.Settings).downstream_mtls.crl": certInfoFromBase64,
"(pomerium.config.Settings).shared_secret": secret,
"(pomerium.config.Settings).cookie_secret": secret,
"(pomerium.config.Settings).google_cloud_serverless_authentication_service_account": secret,
"(pomerium.config.Settings).idp_client_secret": secret,
"(pomerium.config.Settings).databroker_storage_connection_string": secret,
}
type ImportHints struct {
// Indicates that the field is ignored during Zero import
Ignored bool
// Indicates that the field is entirely unsupported by Zero, and will likely
// break an existing configuration if imported. If any of these fields are
// selected, an error will be displayed.
Unsupported bool
// An optional note explaining why a field is ignored or unsupported, if
// additional context would be helpful. This message will be user facing.
Note string
// Indicates that the field is treated as a secret, and will be encrypted.
Secret bool
}
const (
noteSplitService = "split-service mode"
noteEnterpriseOnly = "enterprise only"
noteFeatureNotYetAvailable = "feature not yet available"
)
func noteCertificate(n int) string {
suffix := ""
if n != 1 {
suffix = "s"
}
return fmt.Sprintf("+%d certificate%s", n, suffix)
}
func notePolicy(n int) string {
suffix := "y"
if n != 1 {
suffix = "ies"
}
return fmt.Sprintf("+%d polic%s", n, suffix)
}
func computeSettingsImportHints(cfg *configpb.Config) map[string]ImportHints {
m := map[string]ImportHints{
"authenticate_callback_path": {Ignored: true},
"shared_secret": {Ignored: true},
"cookie_secret": {Ignored: true},
"signing_key": {Ignored: true},
"authenticate_internal_service_url": {Unsupported: true, Note: noteSplitService},
"authorize_internal_service_url": {Unsupported: true, Note: noteSplitService},
"databroker_internal_service_url": {Unsupported: true, Note: noteSplitService},
"derive_tls": {Unsupported: true, Note: noteSplitService},
"audit_key": {Unsupported: true, Note: noteEnterpriseOnly},
"primary_color": {Unsupported: true, Note: noteEnterpriseOnly},
"secondary_color": {Unsupported: true, Note: noteEnterpriseOnly},
"darkmode_primary_color": {Unsupported: true, Note: noteEnterpriseOnly},
"darkmode_secondary_color": {Unsupported: true, Note: noteEnterpriseOnly},
"logo_url": {Unsupported: true, Note: noteEnterpriseOnly},
"favicon_url": {Unsupported: true, Note: noteEnterpriseOnly},
"error_message_first_paragraph": {Unsupported: true, Note: noteEnterpriseOnly},
"use_proxy_protocol": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"programmatic_redirect_domain_whitelist": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"grpc_client_timeout": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"grpc_client_dns_roundrobin": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"envoy_bind_config_freebind": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"envoy_bind_config_source_address": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"google_cloud_serverless_authentication_service_account": {Secret: true},
"idp_client_secret": {Secret: true},
"databroker_storage_connection_string": {Secret: true},
"metrics_certificate": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"metrics_client_ca": {Unsupported: true, Note: noteFeatureNotYetAvailable},
// "metrics_certificate": {Note: noteCertificate(1)},
// "metrics_client_ca": {Note: noteCertificate(1)},
"certificate_authority": {Note: noteCertificate(1)},
"certificates": {Note: noteCertificate(len(cfg.GetSettings().GetCertificates()))},
"downstream_mtls.crl": {Unsupported: true, Note: noteFeatureNotYetAvailable},
"downstream_mtls.ca": {Note: noteCertificate(1)},
}
if dm := cfg.GetSettings().GetDownstreamMtls(); dm != nil {
if dm.Enforcement != nil {
switch *dm.Enforcement {
case configpb.MtlsEnforcementMode_POLICY:
case configpb.MtlsEnforcementMode_POLICY_WITH_DEFAULT_DENY:
case configpb.MtlsEnforcementMode_REJECT_CONNECTION:
// this is a special case - zero does not support this mode, but we cannot continue
// with a partial import because it fundamentally changes the behavior of all routes
// and policies in the system
log.Fatal().Msg("downstream mtls enforcement mode 'reject_connection' is not supported")
}
}
}
if cfg.GetSettings().GetServices() != "all" {
m["services"] = ImportHints{Ignored: true, Note: `only "all" is supported`}
}
if cfg.GetSettings().GetCodecType() != http_connection_managerv3.HttpConnectionManager_AUTO {
m["codec_type"] = ImportHints{Ignored: true, Note: `only "auto" is supported`}
}
return m
}
type ImportUI struct {
form *huh.Form
selectedSettings []string
selectedRoutes []string
}
func NewImportUI(cfg *configpb.Config, quotas *cluster_api.ConfigQuotas) *ImportUI {
settingsImportHints := computeSettingsImportHints(cfg)
presentSettings := fieldmasks.Leaves(
fieldmasks.Diff(
config.NewDefaultOptions().ToProto().GetSettings().ProtoReflect(),
cfg.GetSettings().ProtoReflect(),
),
cfg.Settings.ProtoReflect().Descriptor(),
)
slices.Sort(presentSettings.Paths)
settingsOptions := huh.NewOptions(presentSettings.Paths...)
ui := &ImportUI{
selectedSettings: slices.Clone(presentSettings.Paths),
}
for i, value := range presentSettings.Paths {
if hints, ok := settingsImportHints[value]; ok {
switch {
case hints.Ignored:
note := ""
if hints.Note != "" {
note = fmt.Sprintf(": %s", hints.Note)
}
settingsOptions[i].Key = fmt.Sprintf("\x1b[9m%s\x1b[29m \x1b[2m(ignored%s)\x1b[22m", settingsOptions[i].Key, note)
ui.selectedSettings[i] = ""
case hints.Unsupported:
note := ""
if hints.Note != "" {
note = fmt.Sprintf(": %s", hints.Note)
}
settingsOptions[i].Key = fmt.Sprintf("\x1b[9m%s\x1b[29m \x1b[2m(unsupported%s)\x1b[22m", settingsOptions[i].Key, note)
ui.selectedSettings[i] = ""
case hints.Secret:
settingsOptions[i].Key += " \x1b[2m(secret)\x1b[22m"
default:
if hints.Note != "" {
settingsOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", hints.Note)
}
}
}
}
ui.selectedSettings = slices.DeleteFunc(ui.selectedSettings, func(s string) bool {
return s == ""
})
settingsSelect := huh.NewMultiSelect[string]().
Filterable(false).
Title("Import Settings").
Description("Choose settings to import from your existing configuration").
Options(settingsOptions...).
Validate(func(selected []string) error {
var unsupportedCount int
for _, s := range selected {
if hints, ok := settingsImportHints[s]; ok && hints.Unsupported {
unsupportedCount++
}
}
if unsupportedCount == 1 {
return fmt.Errorf("1 selected setting is unsupported")
} else if unsupportedCount > 1 {
return fmt.Errorf("%d selected settings are unsupported", unsupportedCount)
}
return nil
}).
Value(&ui.selectedSettings)
settingsSelect.Focus()
escapeNoteText := strings.NewReplacer(
"*", "\\*",
"_", "\\_",
"`", "\\`",
)
settingsNoteDescription := func(value string) string {
path, err := paths.ParseFrom(cfg.Settings.ProtoReflect().Descriptor(), "."+value)
if err != nil {
return errText(err)
}
val, err := paths.Evaluate(cfg.Settings, path)
if err != nil {
return errText(err)
}
if infoFunc, ok := customSettingsInfoByPath[path.String()]; ok {
return escapeNoteText.Replace(infoFunc(val))
}
return escapeNoteText.Replace(formatValue(path, val))
}
settingsNote := huh.NewNote().
Title(fmt.Sprintf("Value: %s", presentSettings.Paths[0])).
TitleFunc(func() string {
field, ok := settingsSelect.Hovered()
if !ok {
return ""
}
return fmt.Sprintf("Value: %s", field)
}, onCursorUpdate{settingsSelect}).
Description(settingsNoteDescription(presentSettings.Paths[0])).
DescriptionFunc(func() string {
field, ok := settingsSelect.Hovered()
if !ok {
return ""
}
return settingsNoteDescription(field)
}, onCursorUpdate{settingsSelect}).
Height(3)
settingsNote.Focus()
routeNames := make([]string, len(cfg.Routes))
routesByName := make(map[string]*configpb.Route)
for i, name := range importutil.GenerateRouteNames(cfg.Routes) {
routeNames[i] = name
cfg.Routes[i].Name = name
routesByName[name] = cfg.Routes[i]
}
routeOptions := huh.NewOptions(routeNames...)
for i, name := range routeNames {
if i < quotas.Routes {
ui.selectedRoutes = append(ui.selectedRoutes, name)
}
if n := includedCertificatesInRoute(cfg.Routes[i]); n > 0 {
routeOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", noteCertificate(n))
}
if n := includedPoliciesInRoute(cfg.Routes[i]); n > 0 {
routeOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", notePolicy(n))
}
}
routesSelectDescription := func() string {
return fmt.Sprintf(`
Choose routes to import from your existing configuration. Policies and
certificates associated with selected routes will also be imported.
Pomerium Zero routes require unique names. We've generated default names
from the contents of each route, but these can always be changed later on.
Selected: %d/%d`[1:], len(ui.selectedRoutes), quotas.Routes)
}
topMarginLines := 1 + len(strings.Split(routesSelectDescription(), "\n"))
routesSelect := huh.NewMultiSelect[string]().
Filterable(true).
Title("Import Routes").
Description(routesSelectDescription()).
DescriptionFunc(routesSelectDescription, &ui.selectedRoutes).
Height(min(30, len(cfg.Routes)) + topMarginLines).
Options(routeOptions...).
Validate(func(_ []string) error {
if len(ui.selectedRoutes) > quotas.Routes {
return fmt.Errorf("A maximum of %d routes can be imported", quotas.Routes) //nolint:stylecheck
}
return nil
}).
Value(&ui.selectedRoutes)
var (
labelFrom = yellowText.Render(" from: ")
labelPath = yellowText.Render(" path: ")
labelPrefix = yellowText.Render(" prefix: ")
labelRegex = yellowText.Render(" regex: ")
labelTo = yellowText.Render(" to: ")
labelRedirect = yellowText.Render("redirect: ")
labelResponse = yellowText.Render("response: ")
)
routesNoteDescription := func(selected *configpb.Route) string {
var b strings.Builder
b.WriteString(labelFrom)
b.WriteString(selected.From)
switch {
case selected.Path != "":
b.WriteRune('\n')
b.WriteString(labelPath)
b.WriteString(selected.Path)
case selected.Prefix != "":
b.WriteRune('\n')
b.WriteString(labelPrefix)
b.WriteString(selected.Prefix)
case selected.Regex != "":
b.WriteRune('\n')
b.WriteString(labelRegex)
b.WriteString(selected.Regex)
}
switch {
case len(selected.To) > 0:
b.WriteRune('\n')
b.WriteString(labelTo)
b.WriteString(selected.To[0])
for _, t := range selected.To[1:] {
b.WriteString(", ")
b.WriteString(t)
}
case selected.Redirect != nil:
b.WriteRune('\n')
b.WriteString(labelRedirect)
b.WriteString(selected.Redirect.String())
case selected.Response != nil:
b.WriteRune('\n')
b.WriteString(labelResponse)
b.WriteString(fmt.Sprint(selected.Response.Status))
b.WriteRune(' ')
b.WriteString(strconv.Quote(selected.Response.Body))
}
return b.String()
}
routesNote := huh.NewNote().
Title("Route Info").
Description(routesNoteDescription(cfg.Routes[0])).
DescriptionFunc(func() string {
name, ok := routesSelect.Hovered()
if !ok {
return ""
}
return routesNoteDescription(routesByName[name])
}, onCursorUpdate{routesSelect}).Height(3)
routesNote.Focus()
ui.form = huh.NewForm(
huh.NewGroup(settingsSelect, settingsNote),
huh.NewGroup(routesSelect, routesNote),
).WithTheme(huh.ThemeBase16())
return ui
}
func (ui *ImportUI) Run(ctx context.Context) error {
if lipgloss.ColorProfile() == termenv.Ascii &&
!termenv.EnvNoColor() && os.Getenv("TERM") != "dumb" {
lipgloss.SetColorProfile(termenv.ANSI)
}
return ui.form.RunWithContext(ctx)
}
func (ui *ImportUI) ApplySelections(cfg *configpb.Config) {
fieldmasks.ExclusiveKeep(cfg.Settings, &fieldmaskpb.FieldMask{
Paths: ui.selectedSettings,
})
cfg.Routes = slices.DeleteFunc(cfg.Routes, func(route *configpb.Route) bool {
return !slices.Contains(ui.selectedRoutes, route.Name)
})
}
func includedCertificatesInRoute(route *configpb.Route) int {
n := 0
if route.TlsClientCert != "" && route.TlsClientKey != "" {
n++
}
if route.TlsCustomCa != "" {
n++
}
if route.TlsDownstreamClientCa != "" {
n++
}
return n
}
func includedPoliciesInRoute(route *configpb.Route) int {
n := 0
for _, policy := range route.PplPolicies {
// skip over common generated policies
switch string(policy.Raw) {
case `[{"allow":{"or":[{"accept":true}]}}]`:
case `[{"allow":{"or":[{"authenticated_user":true}]}}]`:
case `[{"allow":{"or":[{"cors_preflight":true}]}}]`:
default:
n++
}
}
return n
}
func formatValue(path protopath.Path, val protoreflect.Value) string {
switch vi := val.Interface().(type) {
case protoreflect.Message:
jsonData, err := protojson.Marshal(vi.Interface())
if err != nil {
return err.Error()
}
return string(jsonData)
case protoreflect.List:
values := []string{}
for i := 0; i < vi.Len(); i++ {
values = append(values, formatValue(path, vi.Get(i)))
}
return renderStringSlice(values)
case protoreflect.Map:
values := []string{}
vi.Range(func(mk protoreflect.MapKey, v protoreflect.Value) bool {
values = append(values, mk.String()+yellowText.Render("=")+formatValue(path, v))
return true
})
slices.Sort(values)
return renderStringSlice(values)
case protoreflect.EnumNumber:
var field protoreflect.FieldDescriptor
switch step := path.Index(-1); step.Kind() {
case protopath.FieldAccessStep:
field = step.FieldDescriptor()
case protopath.ListIndexStep, protopath.MapIndexStep:
field = path.Index(-2).FieldDescriptor()
}
if field != nil {
return strings.ToLower(string(field.Enum().Values().ByNumber(vi).Name()))
}
return fmt.Sprint(vi)
default:
return val.String()
}
}
func renderStringSlice(values []string) string {
return yellowText.Render("[") + strings.Join(values, yellowText.Render(", ")) + yellowText.Render("]")
}

View file

@ -1,125 +0,0 @@
package cmd_test
import (
"bytes"
"compress/gzip"
"context"
"embed"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/pkg/envoy/files"
"github.com/pomerium/pomerium/pkg/zero/cluster"
"github.com/pomerium/pomerium/pkg/zero/importutil"
"github.com/pomerium/protoutil/fieldmasks"
"google.golang.org/protobuf/proto"
"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/exp/teatest"
"github.com/pomerium/pomerium/internal/zero/cmd"
"github.com/stretchr/testify/require"
)
//go:embed testdata
var testdata embed.FS
func TestImportUI(t *testing.T) {
tmp := t.TempDir()
require.NoError(t, os.CopyFS(tmp, testdata))
dir, err := os.Getwd()
require.NoError(t, err)
defer os.Chdir(dir)
os.Chdir(filepath.Join(tmp, "testdata"))
src, err := config.NewFileOrEnvironmentSource("config.yaml", files.FullVersion())
require.NoError(t, err)
cfgC := make(chan *config.Config, 1)
src.OnConfigChange(context.Background(), func(_ context.Context, cfg *config.Config) {
cfgC <- cfg
})
if cfg := src.GetConfig(); cfg != nil {
cfgC <- cfg
}
cfg := (<-cfgC).Options.ToProto()
b, err := proto.Marshal(cfg)
require.NoError(t, err)
var compressed bytes.Buffer
w := gzip.NewWriter(&compressed)
require.NoError(t, err)
w.Write(b)
w.Close()
size := len(compressed.Bytes())
t.Logf("payload size: %d kB", size/1024)
ui := cmd.NewImportUI(cfg, &cluster.ConfigQuotas{
Certificates: 10,
Policies: 10,
Routes: 10,
})
form := ui.XForm()
form.SubmitCmd = tea.Quit
form.CancelCmd = tea.Quit
tm := teatest.NewTestModel(t, form, teatest.WithInitialTermSize(80, 80))
presentSettings := fieldmasks.Leaves(
fieldmasks.Diff(
config.NewDefaultOptions().ToProto().GetSettings().ProtoReflect(),
cfg.GetSettings().ProtoReflect(),
),
cfg.Settings.ProtoReflect().Descriptor(),
)
slices.Sort(presentSettings.Paths)
for i, setting := range presentSettings.Paths {
if i > 0 {
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
}
var foundSelect bool
teatest.WaitFor(t, tm.Output(), func(bts []byte) bool {
str := ansi.Strip(string(bts))
if !foundSelect {
if strings.Contains(str, fmt.Sprintf("> [•] %s", setting)) ||
strings.Contains(str, fmt.Sprintf("> [ ] %s", setting)) {
foundSelect = true
}
return false
}
return strings.Contains(str, fmt.Sprintf("Value: %s", setting))
}, teatest.WithDuration(1*time.Second), teatest.WithCheckInterval(1*time.Millisecond))
}
tm.Send(tea.KeyMsg{Type: tea.KeyTab})
names := importutil.GenerateRouteNames(cfg.Routes)
for i, route := range cfg.Routes {
if i > 0 {
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
}
var foundSelect bool
teatest.WaitFor(t, tm.Output(), func(bts []byte) bool {
str := ansi.Strip(string(bts))
if !foundSelect {
if strings.Contains(str, fmt.Sprintf("> [•] %s", names[i])) ||
strings.Contains(str, fmt.Sprintf("> [ ] %s", names[i])) {
foundSelect = true
}
return false
}
if i == 0 || cfg.Routes[i-1].From != route.From {
return strings.Contains(str, fmt.Sprintf("from: %s", route.From))
}
return true
}, teatest.WithDuration(1*time.Second), teatest.WithCheckInterval(1*time.Millisecond))
}
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
tm.WaitFinished(t)
}

View file

@ -696,7 +696,9 @@ func (r ReportClusterResourceBundleStatusResp) StatusCode() int {
type ImportConfigurationResp struct {
Body []byte
HTTPResponse *http.Response
JSON200 *ImportResponse
JSON400 *ErrorResponse
JSON403 *ErrorResponse
JSON413 *ErrorResponse
JSON500 *ErrorResponse
}
@ -1058,6 +1060,13 @@ func ParseImportConfigurationResp(rsp *http.Response) (*ImportConfigurationResp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest ImportResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
var dest ErrorResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
@ -1065,6 +1074,13 @@ func ParseImportConfigurationResp(rsp *http.Response) (*ImportConfigurationResp,
}
response.JSON400 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest ErrorResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 413:
var dest ErrorResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
@ -1316,6 +1332,11 @@ func (r *ImportConfigurationResp) GetHTTPResponse() *http.Response {
return r.HTTPResponse
}
// GetValue implements apierror.APIResponse
func (r *ImportConfigurationResp) GetValue() *ImportResponse {
return r.JSON200
}
// GetBadRequestError implements apierror.APIResponse
func (r *ImportConfigurationResp) GetBadRequestError() (string, bool) {
if r.JSON400 == nil {
@ -1324,6 +1345,13 @@ func (r *ImportConfigurationResp) GetBadRequestError() (string, bool) {
return r.JSON400.Error, true
}
func (r *ImportConfigurationResp) GetForbiddenError() (string, bool) {
if r.JSON403 == nil {
return "", false
}
return r.JSON403.Error, true
}
// GetInternalServerError implements apierror.APIResponse
func (r *ImportConfigurationResp) GetInternalServerError() (string, bool) {
if r.JSON500 == nil {
@ -1332,14 +1360,6 @@ func (r *ImportConfigurationResp) GetInternalServerError() (string, bool) {
return r.JSON500.Error, true
}
// GetValue implements apierror.APIResponse
func (r *ImportConfigurationResp) GetValue() *EmptyResponse {
if r.StatusCode()/100 != 2 {
return nil
}
return &EmptyResponse{}
}
// GetHTTPResponse implements apierror.APIResponse
func (r *GetQuotasResp) GetHTTPResponse() *http.Response {
return r.HTTPResponse

View file

@ -13,6 +13,6 @@ var (
_ apierror.APIResponse[GetBundlesResponse] = (*GetClusterResourceBundlesResp)(nil)
_ apierror.APIResponse[DownloadBundleResponse] = (*DownloadClusterResourceBundleResp)(nil)
_ apierror.APIResponse[EmptyResponse] = (*ReportClusterResourceBundleStatusResp)(nil)
_ apierror.APIResponse[EmptyResponse] = (*ImportConfigurationResp)(nil)
_ apierror.APIResponse[ImportResponse] = (*ImportConfigurationResp)(nil)
_ apierror.APIResponse[ConfigQuotas] = (*GetQuotasResp)(nil)
)

View file

@ -107,6 +107,12 @@ type GetBundlesResponse struct {
Bundles []Bundle `json:"bundles"`
}
// ImportResponse defines model for ImportResponse.
type ImportResponse struct {
Messages *[]string `json:"messages,omitempty"`
Warnings *[]string `json:"warnings,omitempty"`
}
// ReportUsageRequest defines model for ReportUsageRequest.
type ReportUsageRequest struct {
Users []ReportUsageUser `json:"users"`

View file

@ -196,12 +196,22 @@ paths:
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/ImportResponse"
"400":
description: Bad Request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"403":
description: Forbidden
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: Content Too Large
content:
@ -340,6 +350,17 @@ components:
description: Error message
required:
- error
ImportResponse:
type: object
properties:
messages:
type: array
items:
type: string
warnings:
type: array
items:
type: string
ExchangeTokenRequest:
type: object
properties:

View file

@ -548,12 +548,13 @@ type ImportConfigurationResponseObject interface {
VisitImportConfigurationResponse(w http.ResponseWriter) error
}
type ImportConfiguration200Response struct {
}
type ImportConfiguration200JSONResponse ImportResponse
func (response ImportConfiguration200Response) VisitImportConfigurationResponse(w http.ResponseWriter) error {
func (response ImportConfiguration200JSONResponse) VisitImportConfigurationResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return nil
return json.NewEncoder(w).Encode(response)
}
type ImportConfiguration400JSONResponse ErrorResponse
@ -565,6 +566,15 @@ func (response ImportConfiguration400JSONResponse) VisitImportConfigurationRespo
return json.NewEncoder(w).Encode(response)
}
type ImportConfiguration403JSONResponse ErrorResponse
func (response ImportConfiguration403JSONResponse) VisitImportConfigurationResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
type ImportConfiguration413JSONResponse ErrorResponse
func (response ImportConfiguration413JSONResponse) VisitImportConfigurationResponse(w http.ResponseWriter) error {

View file

@ -225,8 +225,10 @@ func differentiateRoutes(subdomain string, routes []*configpb.Route) iter.Seq2[*
for _, route := range routes {
b.Reset()
b.WriteString(subdomain)
if name != "" {
b.WriteRune('-')
b.WriteString(name)
}
if route.Prefix != "" {
b.WriteString(prefixSuffix)
} else if route.Path != "" {

View file

@ -367,6 +367,23 @@ func TestGenerateRouteNames(t *testing.T) {
yield("test-re-foo-prefix (4)")
}),
},
{
name: "duplicate routes",
input: []*configpb.Route{
{From: "https://route1.localhost.pomerium.io:8443"},
{From: "https://route1.localhost.pomerium.io:8443"},
{From: "https://route2.localhost.pomerium.io:8443"},
{From: "https://route3.localhost.pomerium.io:8443"},
{From: "https://route4.localhost.pomerium.io:8443"},
},
expected: []string{
"route1",
"route1 (2)",
"route2",
"route3",
"route4",
},
},
}
for _, tc := range cases {