mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-30 23:09:23 +02:00
Update core-zero import client
This commit is contained in:
parent
77665603ce
commit
35e4b782ea
15 changed files with 122 additions and 780 deletions
|
@ -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
21
go.mod
|
@ -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
46
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import "github.com/charmbracelet/huh"
|
||||
|
||||
func (ui *ImportUI) XForm() *huh.Form {
|
||||
return ui.form
|
||||
}
|
|
@ -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("]")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 != "" {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue