From 4857bac9b31563a7ea92ab3556c4e0d5961b305b Mon Sep 17 00:00:00 2001 From: Jan Saidl Date: Sun, 23 Jun 2024 16:30:17 +0200 Subject: [PATCH 1/3] z0 - add projest service-add --- go.sum | 101 +++++++++-------- src/cmd/project.go | 3 +- src/cmd/projectServiceAdd.go | 94 +++++++++++++++ src/entity/repository/serviceStackType.go | 83 ++++++++++++++ src/entity/serviceStackType.go | 18 +++ src/i18n/i18n.go | 35 ++++-- src/options/option.go | 32 ++++++ src/uxHelpers/serviceStackType.go | 31 +++++ src/uxHelpers/serviceStackTypeVersion.go | 132 ++++++++++++++++++++++ 9 files changed, 467 insertions(+), 62 deletions(-) create mode 100644 src/cmd/projectServiceAdd.go create mode 100644 src/entity/repository/serviceStackType.go create mode 100644 src/entity/serviceStackType.go create mode 100644 src/options/option.go create mode 100644 src/uxHelpers/serviceStackType.go create mode 100644 src/uxHelpers/serviceStackTypeVersion.go diff --git a/go.sum b/go.sum index d8abd2b7..5cff1433 100644 --- a/go.sum +++ b/go.sum @@ -2,37 +2,29 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= -github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= -github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= -github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= -github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= -github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= -github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA= -github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= -github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= -github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= -github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -52,6 +44,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE 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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -60,29 +53,30 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D 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/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= -github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= -github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= -github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -91,66 +85,73 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zeropsio/zerops-go v1.0.7 h1:vtiaSSu3TrC18BlOxH5/PydUk1+BNDjQhhw6S80SCPE= +github.com/zeropsio/zerops-go v1.0.7/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= +github.com/zeropsio/zerops-go v1.0.8 h1:YhSS7+cW1fIRUE1tD5hpGlD3+opxzvI5lfsONgwdn28= +github.com/zeropsio/zerops-go v1.0.8/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= +github.com/zeropsio/zerops-go v1.0.9 h1:6x0L7RrS33KOf84S75tLM7N/lO7v/ilTA7rkNi8hGz0= +github.com/zeropsio/zerops-go v1.0.9/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= github.com/zeropsio/zerops-go v1.0.10 h1:42zD02HQVKFu8Fc7NC+TtOaSdIIGdzl3Wm1XpR/Brao= github.com/zeropsio/zerops-go v1.0.10/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/cmd/project.go b/src/cmd/project.go index c494b836..ed378586 100644 --- a/src/cmd/project.go +++ b/src/cmd/project.go @@ -13,5 +13,6 @@ func projectCmd() *cmdBuilder.Cmd { AddChildrenCmd(projectListCmd()). AddChildrenCmd(projectDeleteCmd()). AddChildrenCmd(projectServiceImportCmd()). - AddChildrenCmd(projectImportCmd()) + AddChildrenCmd(projectImportCmd()). + AddChildrenCmd(projectServiceAddCmd()) } diff --git a/src/cmd/projectServiceAdd.go b/src/cmd/projectServiceAdd.go new file mode 100644 index 00000000..aa44a8eb --- /dev/null +++ b/src/cmd/projectServiceAdd.go @@ -0,0 +1,94 @@ +package cmd + +import ( + "context" + + "github.com/zeropsio/zcli/src/cmd/scope" + "github.com/zeropsio/zcli/src/cmdBuilder" + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/entity/repository" + "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/uxHelpers" + "github.com/zeropsio/zerops-go/dto/input/body" + "github.com/zeropsio/zerops-go/dto/input/path" + "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/stringId" +) + +const serviceAddArgName = "serviceAddName" +const serviceAddArgType = "type" + +func projectServiceAddCmd() *cmdBuilder.Cmd { + return cmdBuilder.NewCmd(). + Use("service-add"). + Short(i18n.T(i18n.CmdDescProjectServiceAdd)). + ScopeLevel(scope.Project). + Arg(serviceAddArgName). + StringFlag(serviceAddArgType, "", i18n.T(i18n.ServiceTypeFlag)). + HelpFlag(i18n.T(i18n.CmdHelpProjectServiceAdd)). + LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { + name := cmdData.Args[serviceAddArgName][0] + + var typeNameVersion entity.ServiceStackTypeVersion + var typeNameVersionId stringId.ServiceStackTypeVersionId + + if cmdData.Params.GetString(serviceAddArgType) == "" { + serviceStackType, err := uxHelpers.PrintServiceStackTypeSelector(ctx, cmdData.UxBlocks, cmdData.RestApiClient) + if err != nil { + return err + } + if len(serviceStackType.Versions) == 1 { + typeNameVersion = serviceStackType.Versions[0] + } else { + typeNameVersion, err = uxHelpers.PrintServiceStackTypeVersionSelector(ctx, cmdData.UxBlocks, cmdData.RestApiClient, + uxHelpers.PrintServiceStackTypeVersionSelectorWithServiceStackTypeIdFilter(serviceStackType), + ) + if err != nil { + return err + } + } + typeNameVersionId = typeNameVersion.ID + } else { + input := cmdData.Params.GetString(serviceAddArgType) + serviceStackType, err := repository.GetServiceStackTypeById(ctx, cmdData.RestApiClient, stringId.ServiceStackTypeId(input)) + if err != nil { + return err + } + typeNameVersionId = serviceStackType.Versions[0].ID + } + + serviceAddResponse, err := cmdData.RestApiClient.PostServiceStack(ctx, + path.ServiceStackServiceStackTypeVersionId{ + ServiceStackTypeVersionId: typeNameVersionId, + }, + body.PostStandardServiceStack{ + ProjectId: cmdData.Project.ID, + Name: types.NewString(name), + }, + ) + if err != nil { + return err + } + + serviceAddOutput, err := serviceAddResponse.Output() + if err != nil { + return err + } + + err = uxHelpers.ProcessCheckWithSpinner( + ctx, + cmdData.UxBlocks, + []uxHelpers.Process{{ + F: uxHelpers.CheckZeropsProcess(serviceAddOutput.Process.Id, cmdData.RestApiClient), + RunningMessage: i18n.T(i18n.ServiceAdding), + ErrorMessageMessage: i18n.T(i18n.ServiceAddFailed), + SuccessMessage: i18n.T(i18n.ServiceAdded), + }}, + ) + if err != nil { + return err + } + + return nil + }) +} diff --git a/src/entity/repository/serviceStackType.go b/src/entity/repository/serviceStackType.go new file mode 100644 index 00000000..e0ae4606 --- /dev/null +++ b/src/entity/repository/serviceStackType.go @@ -0,0 +1,83 @@ +package repository + +import ( + "context" + "errors" + "sort" + + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/errorCode" + "github.com/zeropsio/zerops-go/types/stringId" +) + +func GetServiceStackTypes( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, +) (result []entity.ServiceStackType, _ error) { + settings, err := restApiClient.GetSettings(ctx) + if err != nil { + return nil, err + } + settingsOutput, err := settings.Output() + if err != nil { + return nil, err + } + for _, serviceStackType := range settingsOutput.ServiceStackList { + e := entity.ServiceStackType{ + ID: serviceStackType.Id, + Name: serviceStackType.Name, + } + + for _, serviceStackTypeVersion := range serviceStackType.ServiceStackTypeVersionList { + if !serviceStackTypeVersion.Status.IsActive() { + continue + } + if serviceStackTypeVersion.IsBuild.Native() { + continue + } + if serviceStackTypeVersion.Name.Native() == "prepare_runtime" { + continue + } + e.Versions = append(e.Versions, entity.ServiceStackTypeVersion{ + ID: serviceStackTypeVersion.Id, + Name: serviceStackTypeVersion.Name, + ExactVersionNumber: serviceStackTypeVersion.ExactVersionNumber, + }) + } + if len(e.Versions) == 0 { + continue + } + result = append(result, e) + } + sort.Slice(result, func(i, j int) bool { return result[i].Name.Native() < result[j].Name.Native() }) + return result, nil +} + +func GetServiceStackTypeById( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, + serviceStackTypeId stringId.ServiceStackTypeId, +) (result entity.ServiceStackType, _ error) { + serviceStackTypes, err := GetServiceStackTypes(ctx, restApiClient) + if err != nil { + return result, err + } + for _, serviceStackType := range serviceStackTypes { + if serviceStackType.ID == serviceStackTypeId { + return serviceStackType, nil + } + if serviceStackType.Name.Native() == serviceStackTypeId.Native() { + return serviceStackType, nil + } + for _, serviceStackTypeVersion := range serviceStackType.Versions { + if serviceStackTypeVersion.ID.Native() == serviceStackTypeId.Native() { + return serviceStackType, nil + } + if serviceStackTypeVersion.Name.Native() == serviceStackTypeId.Native() { + return serviceStackType, nil + } + } + } + return result, errors.New(string(errorCode.ServiceStackTypeVersionNotFound)) +} diff --git a/src/entity/serviceStackType.go b/src/entity/serviceStackType.go new file mode 100644 index 00000000..e9418a1c --- /dev/null +++ b/src/entity/serviceStackType.go @@ -0,0 +1,18 @@ +package entity + +import ( + "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/stringId" +) + +type ServiceStackType struct { + ID stringId.ServiceStackTypeId + Name types.String + Versions []ServiceStackTypeVersion +} + +type ServiceStackTypeVersion struct { + ID stringId.ServiceStackTypeVersionId + Name types.String + ExactVersionNumber types.EmptyString +} diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index a371a60a..7dfe9ae7 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -77,6 +77,13 @@ const ( CmdDescProjectServiceImport = "CmdDescProjectServiceImport" ServiceImported = "ServiceImported" + // project service add + CmdHelpProjectServiceAdd = "CmdHelpProjectServiceAdd" + CmdDescProjectServiceAdd = "CmdDescProjectServiceAdd" + ServiceAdded = "Service added" + ServiceAdding = "Service adding" + ServiceAddFailed = "Service add failed" + // service CmdHelpService = "CmdHelpService" CmdDescService = "CmdDescService" @@ -224,6 +231,7 @@ const ( ConfirmFlag = "ConfirmFlag" ServiceIdFlag = "ServiceIdFlag" ProjectIdFlag = "ProjectIdFlag" + ServiceTypeFlag = "ServiceTypeFlag" VpnAutoDisconnectFlag = "VpnAutoDisconnectFlag" ZeropsYamlSetup = "ZeropsYamlSetup" @@ -277,17 +285,22 @@ const ( ArgsTooManyArgs = "ArgsTooManyArgs" // ux helpers - ProjectSelectorListEmpty = "ProjectSelectorListEmpty" - ProjectSelectorPrompt = "ProjectSelectorPrompt" - ProjectSelectorOutOfRangeError = "ProjectSelectorOutOfRangeError" - ServiceSelectorListEmpty = "ServiceSelectorListEmpty" - ServiceSelectorPrompt = "ServiceSelectorPrompt" - ServiceSelectorOutOfRangeError = "ServiceSelectorOutOfRangeError" - OrgSelectorListEmpty = "OrgSelectorListEmpty" - OrgSelectorPrompt = "OrgSelectorPrompt" - OrgSelectorOutOfRangeError = "OrgSelectorOutOfRangeError" - SelectorAllowedOnlyInTerminal = "SelectorAllowedOnlyInTerminal" - PromptAllowedOnlyInTerminal = "PromptAllowedOnlyInTerminal" + ProjectSelectorListEmpty = "ProjectSelectorListEmpty" + ProjectSelectorPrompt = "ProjectSelectorPrompt" + ProjectSelectorOutOfRangeError = "ProjectSelectorOutOfRangeError" + ServiceSelectorListEmpty = "ServiceSelectorListEmpty" + ServiceStackTypesSelectorListEmpty = "ServiceStackTypeSelectorListEmpty" + ServiceSelectorPrompt = "ServiceSelectorPrompt" + ServiceStackTypeSelectorPrompt = "ServiceStackTypeSelectorPrompt" + ServiceStackTypeSelectorOutOfRangeError = "ServiceStackTypeSelectorOutOfRangeError" + ServiceStackTypeVersionSelectorPrompt = "ServiceStackTypeVersionSelectorPrompt" + ServiceStackTypeVersionSelectorOutOfRangeError = "ServiceStackTypeVersionSelectorOutOfRangeError" + ServiceSelectorOutOfRangeError = "ServiceSelectorOutOfRangeError" + OrgSelectorListEmpty = "OrgSelectorListEmpty" + OrgSelectorPrompt = "OrgSelectorPrompt" + OrgSelectorOutOfRangeError = "OrgSelectorOutOfRangeError" + SelectorAllowedOnlyInTerminal = "SelectorAllowedOnlyInTerminal" + PromptAllowedOnlyInTerminal = "PromptAllowedOnlyInTerminal" UnauthenticatedUser = "UnauthenticatedUser" diff --git a/src/options/option.go b/src/options/option.go new file mode 100644 index 00000000..a85fa0a6 --- /dev/null +++ b/src/options/option.go @@ -0,0 +1,32 @@ +package options + +type OptionError[T any] func(*T) error + +func ApplyOptionsError[K any, T ~func(*K) error](in ...T) (K, []error) { + var emptyValue K + return ApplyOptionsErrorWithDefault[K](emptyValue, in...) +} + +func ApplyOptionsErrorWithDefault[K any, T ~func(*K) error](k K, in ...T) (K, []error) { + var errors []error + for _, o := range in { + if err := o(&k); err != nil { + errors = append(errors, err) + } + } + return k, errors +} + +type Option[T any] func(*T) + +func ApplyOptions[K any, T ~func(*K)](in ...T) K { + var emptyValue K + return ApplyOptionsWithDefault(emptyValue, in...) +} + +func ApplyOptionsWithDefault[K any, T ~func(*K)](k K, in ...T) K { + for _, o := range in { + o(&k) + } + return k +} diff --git a/src/uxHelpers/serviceStackType.go b/src/uxHelpers/serviceStackType.go new file mode 100644 index 00000000..8b1686bc --- /dev/null +++ b/src/uxHelpers/serviceStackType.go @@ -0,0 +1,31 @@ +package uxHelpers + +import ( + "context" + + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/entity/repository" + "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/uxBlock" + "github.com/zeropsio/zcli/src/zeropsRestApiClient" +) + +func PrintServiceStackTypeSelector( + ctx context.Context, + uxBlocks uxBlock.UxBlocks, + restApiClient *zeropsRestApiClient.Handler, +) (result entity.ServiceStackType, _ error) { + serviceStackTypes, err := repository.GetServiceStackTypes(ctx, restApiClient) + if err != nil { + return result, err + } + + return SelectOne(ctx, uxBlocks, serviceStackTypes, + SelectOneWithHeader[entity.ServiceStackType]("ID", "Name"), + SelectOneWithSelectLabel[entity.ServiceStackType](i18n.T(i18n.ServiceStackTypeSelectorPrompt)), + SelectOneWithNotFound[entity.ServiceStackType](i18n.T(i18n.ServiceStackTypeSelectorOutOfRangeError)), + SelectOneWithRow(func(in entity.ServiceStackType) []string { + return []string{string(in.ID), in.Name.String()} + }), + ) +} diff --git a/src/uxHelpers/serviceStackTypeVersion.go b/src/uxHelpers/serviceStackTypeVersion.go new file mode 100644 index 00000000..72b46a4c --- /dev/null +++ b/src/uxHelpers/serviceStackTypeVersion.go @@ -0,0 +1,132 @@ +package uxHelpers + +import ( + "context" + + "github.com/pkg/errors" + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/entity/repository" + "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/options" + "github.com/zeropsio/zcli/src/uxBlock" + "github.com/zeropsio/zcli/src/uxBlock/styles" + "github.com/zeropsio/zcli/src/zeropsRestApiClient" +) + +type printServiceStackTypeVersionSelector struct { + filters []func(entity.ServiceStackType, entity.ServiceStackTypeVersion) bool +} + +func PrintServiceStackTypeVersionSelectorWithServiceStackTypeIdFilter(in entity.ServiceStackType) options.Option[printServiceStackTypeVersionSelector] { + return func(p *printServiceStackTypeVersionSelector) { + p.filters = append(p.filters, func(t entity.ServiceStackType, _ entity.ServiceStackTypeVersion) bool { + return t.ID == in.ID + }) + } +} + +func PrintServiceStackTypeVersionSelector( + ctx context.Context, + uxBlocks uxBlock.UxBlocks, + restApiClient *zeropsRestApiClient.Handler, + opts ...options.Option[printServiceStackTypeVersionSelector], +) (entity.ServiceStackTypeVersion, error) { + setup := options.ApplyOptions(opts...) + + list, err := repository.GetServiceStackTypes(ctx, restApiClient) + if err != nil { + return entity.ServiceStackTypeVersion{}, err + } + var versionList []entity.ServiceStackTypeVersion + for _, typeItem := range list { + for _, versionItem := range typeItem.Versions { + var skipVersion bool + for _, filter := range setup.filters { + if !filter(typeItem, versionItem) { + skipVersion = true + break + } + } + if skipVersion { + continue + } + versionList = append(versionList, versionItem) + } + } + + return SelectOne(ctx, uxBlocks, versionList, + SelectOneWithHeader[entity.ServiceStackTypeVersion]("ID", "Name"), + SelectOneWithSelectLabel[entity.ServiceStackTypeVersion](i18n.T(i18n.ServiceStackTypeVersionSelectorPrompt)), + SelectOneWithNotFound[entity.ServiceStackTypeVersion](i18n.T(i18n.ServiceStackTypeVersionSelectorOutOfRangeError)), + SelectOneWithRow(func(in entity.ServiceStackTypeVersion) []string { + return []string{string(in.ID), in.Name.String()} + }), + ) +} + +type selectOneConfig[T any] struct { + Header []string + Row func(T) []string + SelectLabel string + NotFound string +} + +func SelectOneWithSelectLabel[T any](label string) options.Option[selectOneConfig[T]] { + return func(s *selectOneConfig[T]) { + s.SelectLabel = label + } +} + +func SelectOneWithHeader[T any](columns ...string) options.Option[selectOneConfig[T]] { + return func(s *selectOneConfig[T]) { + s.Header = columns + } +} + +func SelectOneWithRow[T any](in func(T) []string) options.Option[selectOneConfig[T]] { + return func(s *selectOneConfig[T]) { + s.Row = in + } +} + +func SelectOneWithNotFound[T any](label string) options.Option[selectOneConfig[T]] { + return func(s *selectOneConfig[T]) { + s.NotFound = label + } +} + +func SelectOne[T any](ctx context.Context, uxBlocks uxBlock.UxBlocks, list []T, opts ...options.Option[selectOneConfig[T]]) (result T, _ error) { + setup := options.ApplyOptions(opts...) + + if len(list) == 0 { + uxBlocks.PrintWarning(styles.WarningLine(setup.NotFound)) + return result, errors.New(setup.NotFound) + } + header := (&uxBlock.TableRow{}).AddStringCells(setup.Header...) + + tableBody := &uxBlock.TableBody{} + for _, listItem := range list { + tableBody.AddStringsRow(setup.Row(listItem)...) + } + + listIndex, err := uxBlocks.Select( + ctx, + tableBody, + uxBlock.SelectLabel(setup.SelectLabel), + uxBlock.SelectTableHeader(header), + ) + + if err != nil { + return result, err + } + + if len(listIndex) == 0 { + return result, errors.New(setup.NotFound) + } + + if listIndex[0] > len(list)-1 { + return result, errors.New(setup.NotFound) + } + + return list[listIndex[0]], nil +} From 4f0e38e3a361cccf4532f2b6a0faf7e121ff4b8b Mon Sep 17 00:00:00 2001 From: Jan Saidl Date: Sun, 23 Jun 2024 17:25:36 +0200 Subject: [PATCH 2/3] service-add --ha flag --- src/cmd/projectServiceAdd.go | 11 ++++++++++- src/i18n/en.go | 37 ++++++++++++++++++++++++------------ src/i18n/i18n.go | 10 +++++----- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/cmd/projectServiceAdd.go b/src/cmd/projectServiceAdd.go index aa44a8eb..a20a3f92 100644 --- a/src/cmd/projectServiceAdd.go +++ b/src/cmd/projectServiceAdd.go @@ -12,11 +12,13 @@ import ( "github.com/zeropsio/zerops-go/dto/input/body" "github.com/zeropsio/zerops-go/dto/input/path" "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/enum" "github.com/zeropsio/zerops-go/types/stringId" ) const serviceAddArgName = "serviceAddName" const serviceAddArgType = "type" +const serviceAddArgHa = "ha" func projectServiceAddCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). @@ -24,7 +26,8 @@ func projectServiceAddCmd() *cmdBuilder.Cmd { Short(i18n.T(i18n.CmdDescProjectServiceAdd)). ScopeLevel(scope.Project). Arg(serviceAddArgName). - StringFlag(serviceAddArgType, "", i18n.T(i18n.ServiceTypeFlag)). + StringFlag(serviceAddArgType, "", i18n.T(i18n.ServiceAddTypeFlag)). + BoolFlag(serviceAddArgHa, false, i18n.T(i18n.ServiceAddHaFlag)). HelpFlag(i18n.T(i18n.CmdHelpProjectServiceAdd)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { name := cmdData.Args[serviceAddArgName][0] @@ -57,6 +60,11 @@ func projectServiceAddCmd() *cmdBuilder.Cmd { typeNameVersionId = serviceStackType.Versions[0].ID } + mode := enum.ServiceStackModeEnumNonHa + if cmdData.Params.GetBool(serviceAddArgHa) { + mode = enum.ServiceStackModeEnumHa + } + serviceAddResponse, err := cmdData.RestApiClient.PostServiceStack(ctx, path.ServiceStackServiceStackTypeVersionId{ ServiceStackTypeVersionId: typeNameVersionId, @@ -64,6 +72,7 @@ func projectServiceAddCmd() *cmdBuilder.Cmd { body.PostStandardServiceStack{ ProjectId: cmdData.Project.ID, Name: types.NewString(name), + Mode: &mode, }, ) if err != nil { diff --git a/src/i18n/en.go b/src/i18n/en.go index 96837a2e..6cb1e66a 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -77,6 +77,13 @@ and your %s.`, CmdDescProjectServiceImport: "Creates one or more Zerops services in an existing project.", ServiceImported: "service(s) imported", + // project service add + CmdHelpProjectServiceAdd: "the add Zerops service command.", + CmdDescProjectServiceAdd: "Creates one Zerops services in an existing project.", + ServiceAdded: "service added", + ServiceAdding: "service adding", + ServiceAddFailed: "service add failed", + // service CmdHelpService: "the service command.", CmdDescService: "Zerops service commands group", @@ -89,7 +96,7 @@ and your %s.`, ServiceStarted: "Service was started", // service stop - CmdHelpServiceStop: "the enable Zerops subdomain command.", + CmdHelpServiceStop: "the stop Zerops service command.", CmdDescServiceStop: "Starts the Zerops service.", ServiceStopping: "Service is being stopped", ServiceStopFailed: "Service stop failed", @@ -244,6 +251,8 @@ at https://docs.zerops.io/references/cli for further details.`, ConfirmFlag: "If set, zCLI will not ask for confirmation of destructive operations.", ServiceIdFlag: "If you have access to more than one service, you must specify the service ID for which the\ncommand is to be executed.", ProjectIdFlag: "If you have access to more than one project, you must specify the project ID for which the\ncommand is to be executed.", + ServiceAddTypeFlag: "If set, zCLI will add new service with given type.", + ServiceAddHaFlag: "If set, zCLI will add new service with HA mode enabled.", VpnAutoDisconnectFlag: "If set, zCLI will automatically disconnect from the VPN if it is already connected.", ZeropsYamlSetup: "Choose setup to be used from zerops.yml.", @@ -297,17 +306,21 @@ at https://docs.zerops.io/references/cli for further details.`, ArgsTooManyArgs: "expected no more than %d arg(s), got %d", // ux helpers - ProjectSelectorListEmpty: "You don't have any projects yet. Create a new project using `zcli project import` command.", - ProjectSelectorPrompt: "Please, select a project", - ProjectSelectorOutOfRangeError: "We couldn't find a project with the index you entered. Please, try again or contact our support team.", - ServiceSelectorListEmpty: "Project doesn't have any services yet. Create a new service using `zcli service import` command", - ServiceSelectorPrompt: "Please, select a service", - ServiceSelectorOutOfRangeError: "We couldn't find a service with the index you entered. Please, try again or contact our support team.", - OrgSelectorListEmpty: "You don't belong to any organization yet. Please, contact our support team.", - OrgSelectorPrompt: "Please, select an org", - OrgSelectorOutOfRangeError: "We couldn't find an org with the index you entered. Please, try again or contact our support team.", - SelectorAllowedOnlyInTerminal: "Interactive selection can be used only in terminal mode. Use command flags to specify missing parameters.", - PromptAllowedOnlyInTerminal: "Interactive prompt can be used only in terminal mode. Use --confirm=true flag to confirm it", + ProjectSelectorListEmpty: "You don't have any projects yet. Create a new project using `zcli project import` command.", + ProjectSelectorPrompt: "Please, select a project", + ProjectSelectorOutOfRangeError: "We couldn't find a project with the index you entered. Please, try again or contact our support team.", + ServiceSelectorListEmpty: "Project doesn't have any services yet. Create a new service using `zcli service import` command", + ServiceSelectorPrompt: "Please, select a service", + ServiceStackTypeSelectorPrompt: "Please, select a service type", + ServiceStackTypeSelectorOutOfRangeError: "We couldn't find a service type with the index you entered. Please, try again or contact our support team.", + ServiceStackTypeVersionSelectorPrompt: "Please, select a service type version", + ServiceStackTypeVersionSelectorOutOfRangeError: "We couldn't find a service type version with the index you entered. Please, try again or contact our support team.", + ServiceSelectorOutOfRangeError: "We couldn't find a service with the index you entered. Please, try again or contact our support team.", + OrgSelectorListEmpty: "You don't belong to any organization yet. Please, contact our support team.", + OrgSelectorPrompt: "Please, select an org", + OrgSelectorOutOfRangeError: "We couldn't find an org with the index you entered. Please, try again or contact our support team.", + SelectorAllowedOnlyInTerminal: "Interactive selection can be used only in terminal mode. Use command flags to specify missing parameters.", + PromptAllowedOnlyInTerminal: "Interactive prompt can be used only in terminal mode. Use --confirm=true flag to confirm it", UnauthenticatedUser: `unauthenticated user, login before proceeding with this command zcli login {token} diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index 7dfe9ae7..f0eeddb7 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -80,9 +80,9 @@ const ( // project service add CmdHelpProjectServiceAdd = "CmdHelpProjectServiceAdd" CmdDescProjectServiceAdd = "CmdDescProjectServiceAdd" - ServiceAdded = "Service added" - ServiceAdding = "Service adding" - ServiceAddFailed = "Service add failed" + ServiceAdded = "ServiceAdded" + ServiceAdding = "ServiceAdding" + ServiceAddFailed = "ServiceAddFailed" // service CmdHelpService = "CmdHelpService" @@ -231,7 +231,8 @@ const ( ConfirmFlag = "ConfirmFlag" ServiceIdFlag = "ServiceIdFlag" ProjectIdFlag = "ProjectIdFlag" - ServiceTypeFlag = "ServiceTypeFlag" + ServiceAddTypeFlag = "ServiceAddTypeFlag" + ServiceAddHaFlag = "ServiceAddTypeHa" VpnAutoDisconnectFlag = "VpnAutoDisconnectFlag" ZeropsYamlSetup = "ZeropsYamlSetup" @@ -289,7 +290,6 @@ const ( ProjectSelectorPrompt = "ProjectSelectorPrompt" ProjectSelectorOutOfRangeError = "ProjectSelectorOutOfRangeError" ServiceSelectorListEmpty = "ServiceSelectorListEmpty" - ServiceStackTypesSelectorListEmpty = "ServiceStackTypeSelectorListEmpty" ServiceSelectorPrompt = "ServiceSelectorPrompt" ServiceStackTypeSelectorPrompt = "ServiceStackTypeSelectorPrompt" ServiceStackTypeSelectorOutOfRangeError = "ServiceStackTypeSelectorOutOfRangeError" From dfe2957b53ed2fcb65bda2e5ed1a03feaa4e5e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Sun, 23 Jun 2024 23:30:28 +0200 Subject: [PATCH 3/3] feat: service env command --- go.sum | 101 +++++++++--------- src/cmd/service.go | 1 + src/cmd/serviceEnv.go | 129 +++++++++++++++++++++++ src/entity/repository/userData.go | 167 ++++++++++++++++++++++++++++++ src/entity/userData.go | 15 +++ src/i18n/en.go | 53 ++++++---- src/i18n/i18n.go | 52 ++++++---- src/printer/printer.go | 4 + src/uxBlock/blocks.go | 3 + src/uxBlock/logs.go | 18 ++++ src/uxBlock/mocks/blocks.go | 36 +++++++ src/uxBlock/select.go | 2 +- 12 files changed, 485 insertions(+), 96 deletions(-) create mode 100644 src/cmd/serviceEnv.go create mode 100644 src/entity/repository/userData.go create mode 100644 src/entity/userData.go diff --git a/go.sum b/go.sum index 5cff1433..d8abd2b7 100644 --- a/go.sum +++ b/go.sum @@ -2,29 +2,37 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= -github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= -github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= -github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= -github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= +github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= +github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= +github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= +github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -44,7 +52,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE 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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -53,30 +60,29 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D 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/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -85,73 +91,66 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zeropsio/zerops-go v1.0.7 h1:vtiaSSu3TrC18BlOxH5/PydUk1+BNDjQhhw6S80SCPE= -github.com/zeropsio/zerops-go v1.0.7/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= -github.com/zeropsio/zerops-go v1.0.8 h1:YhSS7+cW1fIRUE1tD5hpGlD3+opxzvI5lfsONgwdn28= -github.com/zeropsio/zerops-go v1.0.8/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= -github.com/zeropsio/zerops-go v1.0.9 h1:6x0L7RrS33KOf84S75tLM7N/lO7v/ilTA7rkNi8hGz0= -github.com/zeropsio/zerops-go v1.0.9/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= github.com/zeropsio/zerops-go v1.0.10 h1:42zD02HQVKFu8Fc7NC+TtOaSdIIGdzl3Wm1XpR/Brao= github.com/zeropsio/zerops-go v1.0.10/go.mod h1:Nuqf1xWt53IRLyVoXgR4hF4ICc9jlfOfQgnN3ZhJR3E= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/cmd/service.go b/src/cmd/service.go index 3f9ab873..765c87d2 100644 --- a/src/cmd/service.go +++ b/src/cmd/service.go @@ -15,6 +15,7 @@ func serviceCmd() *cmdBuilder.Cmd { AddChildrenCmd(serviceLogCmd()). AddChildrenCmd(serviceStartCmd()). AddChildrenCmd(serviceStopCmd()). + AddChildrenCmd(serviceEnvCmd()). AddChildrenCmd(servicePushCmd()). AddChildrenCmd(serviceEnableSubdomainCmd()). AddChildrenCmd(serviceDeployCmd()) diff --git a/src/cmd/serviceEnv.go b/src/cmd/serviceEnv.go new file mode 100644 index 00000000..1ffcd413 --- /dev/null +++ b/src/cmd/serviceEnv.go @@ -0,0 +1,129 @@ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + "text/template" + + "github.com/pkg/errors" + "github.com/zeropsio/zcli/src/cmd/scope" + "github.com/zeropsio/zcli/src/cmdBuilder" + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/entity/repository" + "github.com/zeropsio/zerops-go/dto/input/body" + "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/enum" + "gopkg.in/yaml.v3" + + "github.com/zeropsio/zcli/src/i18n" +) + +func serviceEnvCmd() *cmdBuilder.Cmd { + return cmdBuilder.NewCmd(). + Use("env"). + Short(i18n.T(i18n.CmdDescServiceEnv)). + ScopeLevel(scope.Service). + Arg(scope.ServiceArgName, cmdBuilder.OptionalArg()). + StringFlag("format", "env", i18n.T(i18n.ServiceEnvFormatFlag)). + BoolFlag("no-secrets", false, i18n.T(i18n.ServiceEnvNoSecretsFlag)). + HelpFlag(i18n.T(i18n.CmdHelpServiceEnv)). + LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { + var userDataSetup repository.GetUserDataSetup + if cmdData.Params.GetBool("no-secrets") { + userDataSetup.EsFilters(func(filter body.EsFilter) body.EsFilter { + filter.Search = append(filter.Search, body.EsSearchItem{ + Name: "type", + Operator: "ne", + Value: types.String(enum.UserDataTypeEnumSecret), + }) + return filter + }) + } + + userData, err := repository.GetUserDataByProjectId( + ctx, + cmdData.RestApiClient, + cmdData.Project, + userDataSetup, + ) + if err != nil { + return err + } + + allEnvs := make(map[string]entity.UserData, len(userData)) + for _, env := range userData { + allEnvs[fmt.Sprintf("%s_%s", env.ServiceName, env.Key)] = env + } + + envs := make(map[string]entity.UserData) + for key, env := range allEnvs { + c := env.Content.Native() + c = os.Expand(c, func(s string) string { + e, ok := allEnvs[s] + if ok { + return e.Content.Native() + } + return s + }) + env.Content = types.NewText(c) + if env.ServiceId == cmdData.Service.ID { + envs[key] = env + } + } + + format := cmdData.Params.GetString("format") + formatSplit := strings.SplitN(format, "=", 2) + formatKind := formatSplit[0] + + switch formatKind { + case "json": + enc := json.NewEncoder(cmdData.Stdout) + enc.SetIndent("", "\t") + out := make(map[string]string, len(envs)) + for _, e := range envs { + out[e.Key.Native()] = e.Content.Native() + } + if err := enc.Encode(out); err != nil { + return err + } + case "yaml": + enc := yaml.NewEncoder(cmdData.Stdout) + out := make(map[string]string, len(envs)) + for _, e := range envs { + out[e.Key.Native()] = e.Content.Native() + } + if err := enc.Encode(out); err != nil { + return err + } + case "value": + for _, env := range envs { + cmdData.Stdout.Println(env.Content) + } + case "go-template": + if len(formatSplit) < 2 { + return errors.New(i18n.T(i18n.ServiceEnvNoTemplateData)) + } + formatTemplate := formatSplit[1] + t, err := template.New("go").Parse(formatTemplate + "\n") + if err != nil { + return err + } + for _, value := range envs { + if err := t.Execute(cmdData.Stdout, value); err != nil { + return err + } + } + case "env": + for _, env := range envs { + cmdData.Stdout.Printf("%s=%s\n", env.Key, env.Content) + } + default: + return errors.New(i18n.T(i18n.ServiceEnvInvalidFormatKind, formatKind)) + } + + return nil + }) +} diff --git a/src/entity/repository/userData.go b/src/entity/repository/userData.go new file mode 100644 index 00000000..87400927 --- /dev/null +++ b/src/entity/repository/userData.go @@ -0,0 +1,167 @@ +package repository + +import ( + "context" + + "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/errorsx" + "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/options" + "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/apiError" + "github.com/zeropsio/zerops-go/dto/input/body" + "github.com/zeropsio/zerops-go/dto/output" + "github.com/zeropsio/zerops-go/errorCode" + "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/uuid" +) + +func GetUserDataByServiceIdOrName( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, + projectId uuid.ProjectId, + serviceIdOrName string, +) ([]entity.UserData, error) { + project, err := GetProjectById(ctx, restApiClient, projectId) + if err != nil { + return nil, err + } + service, err := GetServiceById(ctx, restApiClient, uuid.ServiceStackId(serviceIdOrName)) + if err != nil { + if errorsx.Is(err, errorsx.Or( + errorsx.ErrorCode(errorCode.InvalidUserInput), + errorsx.ErrorCode(errorCode.ServiceStackNotFound), + )) { + service, err = GetServiceByName(ctx, restApiClient, projectId, types.String(serviceIdOrName)) + if err != nil { + return nil, errorsx.Convert( + err, + errorsx.ErrorCode(errorCode.ServiceStackNotFound, errorsx.ErrorCodeErrorMessage( + func(_ apiError.Error) string { + return i18n.T(i18n.ErrorServiceNotFound, serviceIdOrName) + }, + )), + ) + } + } + } + return GetUserDataByServiceId(ctx, restApiClient, project, service.ID, GetUserDataSetup{}) +} + +type EsFilterFunc func(body.EsFilter) body.EsFilter +type EsFilterFuncs []EsFilterFunc + +func (fs EsFilterFuncs) apply(esFilter body.EsFilter) body.EsFilter { + for _, f := range fs { + esFilter = f(esFilter) + } + return esFilter +} + +type GetUserDataSetup struct { + filters EsFilterFuncs +} + +func UserDataSetup( + opts ...options.Option[GetUserDataSetup], +) GetUserDataSetup { + return options.ApplyOptions(opts...) +} + +func WithUserDataEsFilters(filters ...EsFilterFunc) options.Option[GetUserDataSetup] { + return func(s *GetUserDataSetup) { + s.filters = append(s.filters, filters...) + } +} + +func (s *GetUserDataSetup) EsFilters(filters ...EsFilterFunc) { + s.filters = append(s.filters, filters...) +} + +func GetUserDataByProjectId( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, + project *entity.Project, + setup GetUserDataSetup, +) ([]entity.UserData, error) { + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "projectId", + Operator: "eq", + Value: project.ID.TypedString(), + }, + { + Name: "clientId", + Operator: "eq", + Value: project.OrgId.TypedString(), + }, + }, + } + + return getEsSearchUserData(ctx, restApiClient, setup.filters.apply(esFilter)) +} + +func GetUserDataByServiceId( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, + project *entity.Project, + serviceId uuid.ServiceStackId, + setup GetUserDataSetup, +) ([]entity.UserData, error) { + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "projectId", + Operator: "eq", + Value: project.ID.TypedString(), + }, + { + Name: "clientId", + Operator: "eq", + Value: project.OrgId.TypedString(), + }, + { + Name: "serviceStackId", + Operator: "eq", + Value: serviceId.TypedString(), + }, + }, + } + + return getEsSearchUserData(ctx, restApiClient, setup.filters.apply(esFilter)) +} + +func getEsSearchUserData( + ctx context.Context, + restApiClient *zeropsRestApiClient.Handler, + esFilter body.EsFilter, +) ([]entity.UserData, error) { + userDataResponse, err := restApiClient.PostUserDataSearch(ctx, esFilter) + if err != nil { + return nil, err + } + + userDataOutput, err := userDataResponse.Output() + if err != nil { + return nil, err + } + + userDataResult := make([]entity.UserData, 0, len(userDataOutput.Items)) + for _, userData := range userDataOutput.Items { + userDataResult = append(userDataResult, userDataFromEsSearch(userData)) + } + + return userDataResult, nil +} + +func userDataFromEsSearch(userData output.EsUserData) entity.UserData { + return entity.UserData{ + ID: userData.Id, + ClientId: userData.ClientId, + ServiceId: userData.ServiceStackId, + ServiceName: userData.ServiceStackName, + Key: userData.Key, + Content: userData.Content, + } +} diff --git a/src/entity/userData.go b/src/entity/userData.go new file mode 100644 index 00000000..4bc5acc0 --- /dev/null +++ b/src/entity/userData.go @@ -0,0 +1,15 @@ +package entity + +import ( + "github.com/zeropsio/zerops-go/types" + "github.com/zeropsio/zerops-go/types/uuid" +) + +type UserData struct { + ID uuid.UserDataId + ClientId uuid.ClientId + ServiceId uuid.ServiceStackId + ServiceName types.String + Key types.String + Content types.Text +} diff --git a/src/i18n/en.go b/src/i18n/en.go index 6cb1e66a..08ca9e40 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -109,6 +109,13 @@ and your %s.`, ServiceDeleteFailed: "Service deletion failed", ServiceDeleted: "Service was deleted", + // service env + CmdHelpServiceEnv: "the service env command.", + CmdDescServiceEnv: "Prints expanded envs of service.", + ServiceEnvNoTemplateData: "No format data supplied, see --help", + ServiceEnvInvalidFormatKind: "Invalid format kind %s", + + // service log CmdHelpServiceLog: "the service log command.", CmdDescServiceLog: "Get service runtime or build log to stdout.", @@ -233,28 +240,30 @@ at https://docs.zerops.io/references/cli for further details.`, VpnWgQuickIsNotInstalledWindows: "wireguard is not installed, please visit https://www.wireguard.com/install/", // flags description - RegionFlag: "Choose one of Zerops regions. Use the \"zcli region list\" command to list all Zerops regions.", - RegionUrlFlag: "Zerops region file url.", - BuildVersionName: "Adds a custom version name. Automatically filled if the VERSIONNAME environment variable exists.", - BuildWorkingDir: "Sets a custom working directory. Default working directory is the current directory.", - BuildArchiveFilePath: "If set, zCLI creates a tar.gz archive with the application code in the required path relative\nto the working directory. By default, no archive is created.", - ZeropsYamlLocation: "Sets a custom path to the zerops.yml file relative to the working directory. By default zCLI\nlooks for zerops.yml in the working directory.", - UploadGitFolder: "If set, zCLI the .git folder is also uploaded. By default, the .git folder is ignored.", - OrgIdFlag: "If you have access to more than one organization, you must specify the org ID for which the\nproject is to be created.", - LogLimitFlag: "How many of the most recent log messages will be returned. Allowed interval is <1;1000>.\nDefault value = 100.", - LogMinSeverityFlag: "Returns log messages with requested or higher severity. Set either severity number in the interval\n<0;7> or one of following severity codes:\nEMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFORMATIONAL, DEBUG.", - LogMsgTypeFlag: "Select either APPLICATION or WEBSERVER log messages to be returned. Default value = APPLICATION.", - LogShowBuildFlag: "If set, zCLI will return build log messages instead of runtime log messages.", - LogFollowFlag: "If set, zCLI will continuously poll for new log messages. By default, the command will exit\nonce there are no more logs to display. To exit from this mode, use Control-C.", - LogFormatFlag: "The format of returned log messages. Following formats are supported: \nFULL: This is the default format. Messages will be returned in the complete Syslog format. \nSHORT: Returns only timestamp and log message.\nJSON: Messages will be returned as one JSON object.\nJSONSTREAM: Messages will be returned as stream of JSON objects.", - LogFormatTemplateFlag: "Set a custom log format. Can be used only with --format=FULL.\nExample: --formatTemplate=\"{{.timestamp}} {{.severity}} {{.facility}} {{.message}}\".\nSupports standard GoLang template format and functions.", - ConfirmFlag: "If set, zCLI will not ask for confirmation of destructive operations.", - ServiceIdFlag: "If you have access to more than one service, you must specify the service ID for which the\ncommand is to be executed.", - ProjectIdFlag: "If you have access to more than one project, you must specify the project ID for which the\ncommand is to be executed.", - ServiceAddTypeFlag: "If set, zCLI will add new service with given type.", - ServiceAddHaFlag: "If set, zCLI will add new service with HA mode enabled.", - VpnAutoDisconnectFlag: "If set, zCLI will automatically disconnect from the VPN if it is already connected.", - ZeropsYamlSetup: "Choose setup to be used from zerops.yml.", + RegionFlag: "Choose one of Zerops regions. Use the \"zcli region list\" command to list all Zerops regions.", + RegionUrlFlag: "Zerops region file url.", + BuildVersionName: "Adds a custom version name. Automatically filled if the VERSIONNAME environment variable exists.", + BuildWorkingDir: "Sets a custom working directory. Default working directory is the current directory.", + BuildArchiveFilePath: "If set, zCLI creates a tar.gz archive with the application code in the required path relative\nto the working directory. By default, no archive is created.", + ZeropsYamlLocation: "Sets a custom path to the zerops.yml file relative to the working directory. By default zCLI\nlooks for zerops.yml in the working directory.", + UploadGitFolder: "If set, zCLI the .git folder is also uploaded. By default, the .git folder is ignored.", + OrgIdFlag: "If you have access to more than one organization, you must specify the org ID for which the\nproject is to be created.", + LogLimitFlag: "How many of the most recent log messages will be returned. Allowed interval is <1;1000>.\nDefault value = 100.", + LogMinSeverityFlag: "Returns log messages with requested or higher severity. Set either severity number in the interval\n<0;7> or one of following severity codes:\nEMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFORMATIONAL, DEBUG.", + LogMsgTypeFlag: "Select either APPLICATION or WEBSERVER log messages to be returned. Default value = APPLICATION.", + LogShowBuildFlag: "If set, zCLI will return build log messages instead of runtime log messages.", + LogFollowFlag: "If set, zCLI will continuously poll for new log messages. By default, the command will exit\nonce there are no more logs to display. To exit from this mode, use Control-C.", + LogFormatFlag: "The format of returned log messages. Following formats are supported: \nFULL: This is the default format. Messages will be returned in the complete Syslog format. \nSHORT: Returns only timestamp and log message.\nJSON: Messages will be returned as one JSON object.\nJSONSTREAM: Messages will be returned as stream of JSON objects.", + LogFormatTemplateFlag: "Set a custom log format. Can be used only with --format=FULL.\nExample: --formatTemplate=\"{{.timestamp}} {{.severity}} {{.facility}} {{.message}}\".\nSupports standard GoLang template format and functions.", + ConfirmFlag: "If set, zCLI will not ask for confirmation of destructive operations.", + ServiceIdFlag: "If you have access to more than one service, you must specify the service ID for which the\ncommand is to be executed.", + ProjectIdFlag: "If you have access to more than one project, you must specify the project ID for which the\ncommand is to be executed.", + ServiceAddTypeFlag: "If set, zCLI will add new service with given type.", + ServiceAddHaFlag: "If set, zCLI will add new service with HA mode enabled.", + ServiceEnvFormatFlag: "Format of env output, possible values [env, json, yaml, value, go-template].\nWhen choosing format 'go-template' supply it with template data like --format 'go-template=export -p {{.Key}}={{.Content}}'.\nUseful template values {{.Key}} {{.Content}} {{.ServiceName}} {{.ServiceId}} {{.ClientId}}", + ServiceEnvNoSecretsFlag: "Don't include secrets in command output.", + VpnAutoDisconnectFlag: "If set, zCLI will automatically disconnect from the VPN if it is already connected.", + ZeropsYamlSetup: "Choose setup to be used from zerops.yml.", // archiveClient ArchClientWorkingDirectory: "working directory: %s", diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index f0eeddb7..e3c91050 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -95,6 +95,12 @@ const ( ServiceStartFailed = "ServiceStartFailed" ServiceStarted = "ServiceStarted" + // service env + CmdDescServiceEnv = "CmdDescServiceEnv" + CmdHelpServiceEnv = "CmdHelpServiceEnv" + ServiceEnvNoTemplateData = "ServiceEnvNoTemplateData" + ServiceEnvInvalidFormatKind = "ServiceEnvInvalidFormatKind" + // service stop CmdHelpServiceStop = "CmdHelpServiceStop" CmdDescServiceStop = "CmdDescServiceStop" @@ -213,28 +219,30 @@ const ( VpnWgQuickIsNotInstalledWindows = "VpnWgQuickIsNotInstalledWindows" // flags description - RegionFlag = "RegionFlag" - RegionUrlFlag = "RegionUrlFlag" - BuildVersionName = "BuildVersionName" - BuildWorkingDir = "BuildWorkingDir" - BuildArchiveFilePath = "BuildArchiveFilePath" - ZeropsYamlLocation = "ZeropsYamlLocation" - UploadGitFolder = "UploadGitFolder" - OrgIdFlag = "OrgIdFlag" - LogLimitFlag = "LogLimitFlag" - LogMinSeverityFlag = "LogMinSeverityFlag" - LogMsgTypeFlag = "LogMsgTypeFlag" - LogFollowFlag = "LogFollowFlag" - LogShowBuildFlag = "LogShowBuildFlag" - LogFormatFlag = "LogFormatFlag" - LogFormatTemplateFlag = "LogFormatTemplateFlag" - ConfirmFlag = "ConfirmFlag" - ServiceIdFlag = "ServiceIdFlag" - ProjectIdFlag = "ProjectIdFlag" - ServiceAddTypeFlag = "ServiceAddTypeFlag" - ServiceAddHaFlag = "ServiceAddTypeHa" - VpnAutoDisconnectFlag = "VpnAutoDisconnectFlag" - ZeropsYamlSetup = "ZeropsYamlSetup" + RegionFlag = "RegionFlag" + RegionUrlFlag = "RegionUrlFlag" + BuildVersionName = "BuildVersionName" + BuildWorkingDir = "BuildWorkingDir" + BuildArchiveFilePath = "BuildArchiveFilePath" + ZeropsYamlLocation = "ZeropsYamlLocation" + UploadGitFolder = "UploadGitFolder" + OrgIdFlag = "OrgIdFlag" + LogLimitFlag = "LogLimitFlag" + LogMinSeverityFlag = "LogMinSeverityFlag" + LogMsgTypeFlag = "LogMsgTypeFlag" + LogFollowFlag = "LogFollowFlag" + LogShowBuildFlag = "LogShowBuildFlag" + LogFormatFlag = "LogFormatFlag" + LogFormatTemplateFlag = "LogFormatTemplateFlag" + ConfirmFlag = "ConfirmFlag" + ServiceIdFlag = "ServiceIdFlag" + ProjectIdFlag = "ProjectIdFlag" + ServiceAddTypeFlag = "ServiceAddTypeFlag" + ServiceAddHaFlag = "ServiceAddTypeHa" + ServiceEnvFormatFlag = "ServiceEnvFormatFlag" + ServiceEnvNoSecretsFlag = "ServiceEnvNoSecretsFlag" + VpnAutoDisconnectFlag = "VpnAutoDisconnectFlag" + ZeropsYamlSetup = "ZeropsYamlSetup" // archiveClient ArchClientWorkingDirectory = "ArchClientWorkingDirectory" diff --git a/src/printer/printer.go b/src/printer/printer.go index 8a0108fb..138d9ec9 100644 --- a/src/printer/printer.go +++ b/src/printer/printer.go @@ -24,6 +24,10 @@ func NewPrinter(out io.Writer) Printer { } } +func (p Printer) Write(d []byte) (int, error) { + return p.out.Write(d) +} + func (p Printer) Printf(format string, args ...any) { fmt.Fprintf(p.out, format, args...) } diff --git a/src/uxBlock/blocks.go b/src/uxBlock/blocks.go index e271e989..254ddaa9 100644 --- a/src/uxBlock/blocks.go +++ b/src/uxBlock/blocks.go @@ -13,8 +13,11 @@ import ( type UxBlocks interface { LogDebug(message string) PrintInfo(line styles.Line) + PrintInfoLine(text string) PrintWarning(line styles.Line) + PrintWarningLine(text string) PrintError(line styles.Line) + PrintErrorLine(text string) Table(body *TableBody, auxOptions ...TableOption) Select(ctx context.Context, tableBody *TableBody, auxOptions ...SelectOption) ([]int, error) Prompt( diff --git a/src/uxBlock/logs.go b/src/uxBlock/logs.go index 315f6503..39c2ed06 100644 --- a/src/uxBlock/logs.go +++ b/src/uxBlock/logs.go @@ -11,12 +11,30 @@ func (b *uxBlocks) PrintInfo(line styles.Line) { b.debugFileLogger.Info(line.DisableStyle()) } +func (b *uxBlocks) PrintInfoLine(text string) { + line := styles.InfoLine(text) + b.outputLogger.Info(line) + b.debugFileLogger.Info(line.DisableStyle()) +} + func (b *uxBlocks) PrintWarning(line styles.Line) { b.outputLogger.Warning(line) b.debugFileLogger.Warning(line.DisableStyle()) } +func (b *uxBlocks) PrintWarningLine(text string) { + line := styles.WarningLine(text) + b.outputLogger.Warning(line) + b.debugFileLogger.Warning(line.DisableStyle()) +} + func (b *uxBlocks) PrintError(line styles.Line) { b.outputLogger.Error(line) b.debugFileLogger.Error(line.DisableStyle()) } + +func (b *uxBlocks) PrintErrorLine(text string) { + line := styles.ErrorLine(text) + b.outputLogger.Error(line) + b.debugFileLogger.Error(line.DisableStyle()) +} diff --git a/src/uxBlock/mocks/blocks.go b/src/uxBlock/mocks/blocks.go index 045ec2d0..59bfecfc 100644 --- a/src/uxBlock/mocks/blocks.go +++ b/src/uxBlock/mocks/blocks.go @@ -60,6 +60,18 @@ func (mr *MockUxBlocksMockRecorder) PrintError(line interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintError", reflect.TypeOf((*MockUxBlocks)(nil).PrintError), line) } +// PrintErrorLine mocks base method. +func (m *MockUxBlocks) PrintErrorLine(text string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PrintErrorLine", text) +} + +// PrintErrorLine indicates an expected call of PrintErrorLine. +func (mr *MockUxBlocksMockRecorder) PrintErrorLine(text interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintErrorLine", reflect.TypeOf((*MockUxBlocks)(nil).PrintErrorLine), text) +} + // PrintInfo mocks base method. func (m *MockUxBlocks) PrintInfo(line styles.Line) { m.ctrl.T.Helper() @@ -72,6 +84,18 @@ func (mr *MockUxBlocksMockRecorder) PrintInfo(line interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintInfo", reflect.TypeOf((*MockUxBlocks)(nil).PrintInfo), line) } +// PrintInfoLine mocks base method. +func (m *MockUxBlocks) PrintInfoLine(text string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PrintInfoLine", text) +} + +// PrintInfoLine indicates an expected call of PrintInfoLine. +func (mr *MockUxBlocksMockRecorder) PrintInfoLine(text interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintInfoLine", reflect.TypeOf((*MockUxBlocks)(nil).PrintInfoLine), text) +} + // PrintWarning mocks base method. func (m *MockUxBlocks) PrintWarning(line styles.Line) { m.ctrl.T.Helper() @@ -84,6 +108,18 @@ func (mr *MockUxBlocksMockRecorder) PrintWarning(line interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintWarning", reflect.TypeOf((*MockUxBlocks)(nil).PrintWarning), line) } +// PrintWarningLine mocks base method. +func (m *MockUxBlocks) PrintWarningLine(text string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PrintWarningLine", text) +} + +// PrintWarningLine indicates an expected call of PrintWarningLine. +func (mr *MockUxBlocksMockRecorder) PrintWarningLine(text interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintWarningLine", reflect.TypeOf((*MockUxBlocks)(nil).PrintWarningLine), text) +} + // Prompt mocks base method. func (m *MockUxBlocks) Prompt(ctx context.Context, message string, choices []string, auxOptions ...uxBlock.PromptOption) (int, error) { m.ctrl.T.Helper() diff --git a/src/uxBlock/select.go b/src/uxBlock/select.go index bbdcec25..9984e01f 100644 --- a/src/uxBlock/select.go +++ b/src/uxBlock/select.go @@ -199,5 +199,5 @@ func (m *selectModel) View() string { t.Width(calculateTableWidth(t, m.uxBlocks.terminalWidth)) - return s + t.String() + return s + t.String() + "\n" }