diff --git a/Makefile b/Makefile index 9a4b1487..f6d71478 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ define gen-doc-in-dir cat ./$1/koyeb_sandbox_*.md >> ./$1/reference.md cat ./$1/koyeb_version.md >> ./$1/reference.md cat ./$1/koyeb_volumes.md >> ./$1/reference.md + cat ./$1/koyeb_projects.md >> ./$1/reference.md + cat ./$1/koyeb_projects_*.md >> ./$1/reference.md + find ./$1 -type f -not -name 'reference.md' -delete endef diff --git a/docs/reference.md b/docs/reference.md index 34238b36..fb690cab 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -24,6 +24,7 @@ Koyeb CLI -h, --help help for koyeb --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -42,6 +43,7 @@ Koyeb CLI * [koyeb login](#koyeb-login) - Login to your Koyeb account * [koyeb metrics](#koyeb-metrics) - Metrics * [koyeb organizations](#koyeb-organizations) - Organization +* [koyeb projects](#koyeb-projects) - Projects * [koyeb regional-deployments](#koyeb-regional-deployments) - Regional deployments * [koyeb sandbox](#koyeb-sandbox) - Sandbox - interactive execution environments * [koyeb secrets](#koyeb-secrets) - Secrets @@ -49,6 +51,7 @@ Koyeb CLI * [koyeb snapshots](#koyeb-snapshots) - Manage snapshots * [koyeb version](#koyeb-version) - Get version * [koyeb volumes](#koyeb-volumes) - Manage persistent volumes +* [koyeb whoami](#koyeb-whoami) - Show information about the currently authenticated user or organization ## koyeb login @@ -74,6 +77,7 @@ koyeb login [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -102,6 +106,7 @@ Apps --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -144,6 +149,7 @@ koyeb apps create NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -176,6 +182,7 @@ koyeb apps delete NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -208,6 +215,7 @@ koyeb apps describe NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -240,6 +248,7 @@ koyeb apps get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -369,6 +378,7 @@ See examples of koyeb service create --help --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -388,7 +398,8 @@ koyeb apps list [flags] ### Options ``` - -h, --help help for list + --all-projects List apps from all projects (overrides project filter) + -h, --help help for list ``` ### Options inherited from parent commands @@ -401,6 +412,7 @@ koyeb apps list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -433,6 +445,7 @@ koyeb apps pause NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -465,6 +478,7 @@ koyeb apps resume NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -500,6 +514,7 @@ koyeb apps update NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -528,6 +543,7 @@ Archives --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -564,6 +580,7 @@ koyeb archives create NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -668,6 +685,7 @@ koyeb deploy / [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -696,6 +714,7 @@ Domains --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -736,6 +755,7 @@ koyeb domains attach NAME APP [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -769,6 +789,7 @@ koyeb domains create NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -801,6 +822,7 @@ koyeb domains delete [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -833,6 +855,7 @@ koyeb domains describe [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -865,6 +888,7 @@ koyeb domains detach NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -897,6 +921,7 @@ koyeb domains get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -929,6 +954,7 @@ koyeb domains list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -961,6 +987,7 @@ koyeb domains refresh NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -989,6 +1016,7 @@ Organization --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1023,6 +1051,7 @@ koyeb organizations list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1055,6 +1084,7 @@ koyeb organizations switch [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1083,6 +1113,7 @@ Secrets --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1129,6 +1160,7 @@ koyeb secrets create NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1161,6 +1193,7 @@ koyeb secrets delete NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1193,6 +1226,7 @@ koyeb secrets describe NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1225,6 +1259,7 @@ koyeb secrets get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1257,6 +1292,7 @@ koyeb secrets list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1289,6 +1325,7 @@ koyeb secrets reveal NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1327,6 +1364,7 @@ koyeb secrets update NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1355,6 +1393,7 @@ Services --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1513,6 +1552,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1546,6 +1586,7 @@ koyeb services delete NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1579,6 +1620,7 @@ koyeb services describe NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1612,6 +1654,7 @@ koyeb services exec NAME CMD -- [args...] [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1645,6 +1688,7 @@ koyeb services get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1679,6 +1723,7 @@ koyeb services list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1721,6 +1766,7 @@ koyeb services logs NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1754,6 +1800,7 @@ koyeb services pause NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1791,6 +1838,7 @@ koyeb services redeploy NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1824,6 +1872,7 @@ koyeb services resume NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -1857,6 +1906,7 @@ koyeb services unapplied-changes SERVICE_NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2001,6 +2051,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2055,6 +2106,7 @@ $> koyeb service scale app/podinfo --scale fra:5 --scale was:3 --scale sin:2 --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2100,6 +2152,7 @@ $> koyeb service scale delete app/podinfo --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2133,6 +2186,7 @@ koyeb services scale get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2193,6 +2247,7 @@ $> koyeb service scale update app/podinfo --scale fra:5 --scale '!was' --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2221,6 +2276,7 @@ Deployments --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2258,6 +2314,7 @@ koyeb deployments cancel NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2290,6 +2347,7 @@ koyeb deployments describe NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2322,6 +2380,7 @@ koyeb deployments get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2356,6 +2415,7 @@ koyeb deployments list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2396,6 +2456,7 @@ koyeb deployments logs NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2424,6 +2485,7 @@ Instances --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2472,6 +2534,7 @@ $> koyeb instance cp :/tmp/spreadsheet.csv . --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2504,6 +2567,7 @@ koyeb instances describe NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2536,6 +2600,7 @@ koyeb instances exec NAME CMD -- [args...] [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2568,6 +2633,7 @@ koyeb instances get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2602,6 +2668,7 @@ koyeb instances list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2641,6 +2708,7 @@ koyeb instances logs NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2669,6 +2737,7 @@ Databases --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2713,6 +2782,7 @@ koyeb databases create NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2745,6 +2815,7 @@ koyeb databases delete NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2778,6 +2849,7 @@ koyeb databases get NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2810,6 +2882,7 @@ koyeb databases list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2845,6 +2918,7 @@ koyeb databases update NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2881,6 +2955,7 @@ managing processes, filesystem operations, and port exposure. --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -2960,6 +3035,7 @@ $> koyeb sandbox create myapp/mysandbox --wait --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3001,6 +3077,7 @@ $> koyeb sandbox expose-port myapp/mysandbox 8080 --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3009,41 +3086,6 @@ $> koyeb sandbox expose-port myapp/mysandbox 8080 * [koyeb sandbox](#koyeb-sandbox) - Sandbox - interactive execution environments -## koyeb sandbox fs - -Filesystem operations - -### Options - -``` - -h, --help help for fs -``` - -### Options inherited from parent commands - -``` - -c, --config string config file (default is $HOME/.koyeb.yaml) - -d, --debug enable the debug output - --debug-full do not hide sensitive information (tokens) in the debug output - --force-ascii only output ascii characters (no unicode emojis) - --full do not truncate output - --organization string organization ID - -o, --output output output format (yaml,json,table) - --token string API token - --url string url of the api (default "https://app.koyeb.com") -``` - - - -* [koyeb sandbox](#koyeb-sandbox) - Sandbox - interactive execution environments -* [koyeb sandbox fs download](#koyeb-sandbox-fs-download) - Download a file from the sandbox -* [koyeb sandbox fs ls](#koyeb-sandbox-fs-ls) - List directory contents in the sandbox -* [koyeb sandbox fs mkdir](#koyeb-sandbox-fs-mkdir) - Create a directory in the sandbox -* [koyeb sandbox fs read](#koyeb-sandbox-fs-read) - Read a file from the sandbox -* [koyeb sandbox fs rm](#koyeb-sandbox-fs-rm) - Remove a file or directory from the sandbox -* [koyeb sandbox fs upload](#koyeb-sandbox-fs-upload) - Upload a local file or directory to the sandbox (max 1G per file) -* [koyeb sandbox fs write](#koyeb-sandbox-fs-write) - Write content to a file in the sandbox - ## koyeb sandbox fs download Download a file from the sandbox @@ -3068,6 +3110,7 @@ koyeb sandbox fs download NAME REMOTE_PATH LOCAL_PATH [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3101,6 +3144,7 @@ koyeb sandbox fs ls NAME [PATH] [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3133,6 +3177,7 @@ koyeb sandbox fs mkdir NAME PATH [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3165,6 +3210,7 @@ koyeb sandbox fs read NAME PATH [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3198,6 +3244,7 @@ koyeb sandbox fs rm NAME PATH [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3237,6 +3284,7 @@ koyeb sandbox fs upload NAME LOCAL_PATH REMOTE_PATH [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3288,6 +3336,7 @@ $> koyeb sandbox fs write myapp/mysandbox /tmp/script.py -f ./local-script.py --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3296,6 +3345,42 @@ $> koyeb sandbox fs write myapp/mysandbox /tmp/script.py -f ./local-script.py * [koyeb sandbox fs](#koyeb-sandbox-fs) - Filesystem operations +## koyeb sandbox fs + +Filesystem operations + +### Options + +``` + -h, --help help for fs +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb sandbox](#koyeb-sandbox) - Sandbox - interactive execution environments +* [koyeb sandbox fs download](#koyeb-sandbox-fs-download) - Download a file from the sandbox +* [koyeb sandbox fs ls](#koyeb-sandbox-fs-ls) - List directory contents in the sandbox +* [koyeb sandbox fs mkdir](#koyeb-sandbox-fs-mkdir) - Create a directory in the sandbox +* [koyeb sandbox fs read](#koyeb-sandbox-fs-read) - Read a file from the sandbox +* [koyeb sandbox fs rm](#koyeb-sandbox-fs-rm) - Remove a file or directory from the sandbox +* [koyeb sandbox fs upload](#koyeb-sandbox-fs-upload) - Upload a local file or directory to the sandbox (max 1G per file) +* [koyeb sandbox fs write](#koyeb-sandbox-fs-write) - Write content to a file in the sandbox + ## koyeb sandbox health Check sandbox health status @@ -3320,6 +3405,7 @@ koyeb sandbox health NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3352,6 +3438,7 @@ koyeb sandbox kill NAME PROCESS_ID [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3386,6 +3473,7 @@ koyeb sandbox list [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3419,6 +3507,7 @@ koyeb sandbox logs NAME PROCESS_ID [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3451,6 +3540,7 @@ koyeb sandbox ps NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3511,6 +3601,7 @@ $> koyeb sandbox run myapp/mysandbox --timeout 120 long-running-command --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3557,6 +3648,7 @@ $> koyeb sandbox start myapp/mysandbox --cwd /app npm start --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3589,6 +3681,7 @@ koyeb sandbox unexpose-port NAME [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3621,6 +3714,7 @@ koyeb version [flags] --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3649,6 +3743,7 @@ Manage persistent volumes --full do not truncate output --organization string organization ID -o, --output output output format (yaml,json,table) + -p, --project string project ID --token string API token --url string url of the api (default "https://app.koyeb.com") ``` @@ -3662,3 +3757,273 @@ Manage persistent volumes * [koyeb volumes list](#koyeb-volumes-list) - List volumes * [koyeb volumes update](#koyeb-volumes-update) - Update a volume +## koyeb projects + +Projects + +### Options + +``` + -h, --help help for projects +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb](#koyeb) - Koyeb CLI +* [koyeb projects create](#koyeb-projects-create) - Create project +* [koyeb projects delete](#koyeb-projects-delete) - Delete project +* [koyeb projects describe](#koyeb-projects-describe) - Describe project +* [koyeb projects get](#koyeb-projects-get) - Get project +* [koyeb projects list](#koyeb-projects-list) - List projects +* [koyeb projects switch](#koyeb-projects-switch) - Switch the default project for the CLI context +* [koyeb projects update](#koyeb-projects-update) - Update project + +## koyeb projects create + +Create project + +``` +koyeb projects create NAME [flags] +``` + +### Options + +``` + --description string Project description + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects delete + +Delete project + +``` +koyeb projects delete NAME [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects describe + +Describe project + +``` +koyeb projects describe NAME [flags] +``` + +### Options + +``` + -h, --help help for describe +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects get + +Get project + +``` +koyeb projects get NAME [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects list + +List projects + +``` +koyeb projects list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects switch + +Switch the default project for the CLI context + +``` +koyeb projects switch NAME [flags] +``` + +### Options + +``` + -h, --help help for switch +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + +## koyeb projects update + +Update project + +``` +koyeb projects update NAME [flags] +``` + +### Options + +``` + --description string Project description + -h, --help help for update + -n, --name string Project name +``` + +### Options inherited from parent commands + +``` + -c, --config string config file (default is $HOME/.koyeb.yaml) + -d, --debug enable the debug output + --debug-full do not hide sensitive information (tokens) in the debug output + --force-ascii only output ascii characters (no unicode emojis) + --full do not truncate output + --organization string organization ID + -o, --output output output format (yaml,json,table) + -p, --project string project ID + --token string API token + --url string url of the api (default "https://app.koyeb.com") +``` + + + +* [koyeb projects](#koyeb-projects) - Projects + diff --git a/pkg/koyeb/apps.go b/pkg/koyeb/apps.go index 67832a50..fd69a34a 100644 --- a/pkg/koyeb/apps.go +++ b/pkg/koyeb/apps.go @@ -38,6 +38,7 @@ func NewAppCmd() *cobra.Command { }), } createAppCmd.Flags().Bool("delete-when-empty", false, "Automatically delete the app after the last service is deleted. Empty apps created without services are not deleted.") + createAppCmd.Flags().StringP("project", "p", "", "Project name or ID") appCmd.AddCommand(createAppCmd) initAppCmd := &cobra.Command{ @@ -64,6 +65,7 @@ func NewAppCmd() *cobra.Command { } initAppCmd.Flags().Bool("wait", false, "Waits until app deployment is done") initAppCmd.Flags().Duration("wait-timeout", 5*time.Minute, "Duration the wait will last until timeout") + initAppCmd.Flags().StringP("project", "p", "", "Project name or ID") appCmd.AddCommand(initAppCmd) serviceHandler.addServiceDefinitionFlags(initAppCmd.Flags()) @@ -73,6 +75,7 @@ func NewAppCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Get), } + getAppCmd.Flags().StringP("project", "p", "", "Project name or ID (optional context)") appCmd.AddCommand(getAppCmd) listAppCmd := &cobra.Command{ @@ -80,6 +83,8 @@ func NewAppCmd() *cobra.Command { Short: "List apps", RunE: WithCLIContext(h.List), } + listAppCmd.Flags().StringP("project", "p", "", "Project name or ID (filter apps by project)") + listAppCmd.Flags().Bool("all-projects", false, "List apps from all projects (overrides project filter)") appCmd.AddCommand(listAppCmd) describeAppCmd := &cobra.Command{ @@ -88,6 +93,7 @@ func NewAppCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Describe), } + describeAppCmd.Flags().StringP("project", "p", "", "Project name or ID (optional context)") appCmd.AddCommand(describeAppCmd) updateAppCmd := &cobra.Command{ @@ -130,6 +136,7 @@ func NewAppCmd() *cobra.Command { updateAppCmd.Flags().StringP("name", "n", "", "Change the name of the app") updateAppCmd.Flags().StringP("domain", "D", "", "Change the subdomain of the app (only specify the subdomain, skipping \".koyeb.app\")") updateAppCmd.Flags().Bool("delete-when-empty", false, "Automatically delete the app after the last service is deleted. Empty apps created without services are not deleted.") + updateAppCmd.Flags().StringP("project", "p", "", "Project name or ID (optional context)") appCmd.AddCommand(updateAppCmd) deleteAppCmd := &cobra.Command{ @@ -138,6 +145,7 @@ func NewAppCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Delete), } + deleteAppCmd.Flags().StringP("project", "p", "", "Project name or ID (optional context)") appCmd.AddCommand(deleteAppCmd) pauseServiceCmd := &cobra.Command{ diff --git a/pkg/koyeb/apps_create.go b/pkg/koyeb/apps_create.go index 4ddf5a9c..c11c2623 100644 --- a/pkg/koyeb/apps_create.go +++ b/pkg/koyeb/apps_create.go @@ -23,6 +23,16 @@ func (h *AppHandler) CreateApp(ctx *CLIContext, payload *koyeb.CreateApp) (*koye func (h *AppHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []string, createApp *koyeb.CreateApp) error { createApp.SetName(args[0]) + projectFlag, _ := cmd.Flags().GetString("project") + if projectFlag != "" { + projectHandler := NewProjectHandler() + projectID, err := projectHandler.ResolveProjectArgs(ctx, projectFlag) + if err != nil { + return err + } + createApp.SetProjectId(projectID) + } + res, err := h.CreateApp(ctx, createApp) if err != nil { return err diff --git a/pkg/koyeb/apps_list.go b/pkg/koyeb/apps_list.go index 6cad0b9e..a5585a75 100644 --- a/pkg/koyeb/apps_list.go +++ b/pkg/koyeb/apps_list.go @@ -13,6 +13,21 @@ import ( func (h *AppHandler) List(ctx *CLIContext, cmd *cobra.Command, args []string) error { list := []koyeb.AppListItem{} + // TODO: Implement project filtering when API support is available + // Check if we should filter by project + // allProjects, _ := cmd.Flags().GetBool("all-projects") + // projectFlag, _ := cmd.Flags().GetString("project") + // + // var projectID string + // if !allProjects && projectFlag != "" { + // projectHandler := NewProjectHandler() + // var err error + // projectID, err = projectHandler.ResolveProjectArgs(ctx, projectFlag) + // if err != nil { + // return err + // } + // } + page := int64(0) offset := int64(0) limit := int64(100) diff --git a/pkg/koyeb/context.go b/pkg/koyeb/context.go index 85f47289..bee7fb23 100644 --- a/pkg/koyeb/context.go +++ b/pkg/koyeb/context.go @@ -18,11 +18,14 @@ const ( ctx_mapper ctx_renderer ctx_organization + ctx_project ) // SetupCLIContext is called by the root command to setup the context for all subcommands. // When `organization` is not empty, it should contain the ID of the organization to switch the context to. -func SetupCLIContext(cmd *cobra.Command, organization string) error { +// When `project` is not empty, it should contain the ID of the project to use in the context. +// If the project is invalid or doesn't exist in the current organization, it will be cleared. +func SetupCLIContext(cmd *cobra.Command, organization string, project string) error { apiClient, err := getApiClient() if err != nil { return err @@ -60,6 +63,17 @@ func SetupCLIContext(cmd *cobra.Command, organization string) error { ctx = context.WithValue(ctx, ctx_mapper, idmapper.NewMapper(ctx, apiClient)) ctx = context.WithValue(ctx, ctx_renderer, renderer.NewRenderer(outputFormat)) ctx = context.WithValue(ctx, ctx_organization, organization) + + // Validate project exists in current organization. If not, clear it as fallback. + if project != "" { + projectMapper := idmapper.NewMapper(ctx, apiClient).Project() + if _, err := projectMapper.ResolveID(project); err != nil { + // Project is invalid or doesn't exist - clear it silently as fallback + project = "" + } + } + + ctx = context.WithValue(ctx, ctx_project, project) cmd.SetContext(ctx) return nil @@ -74,6 +88,7 @@ type CLIContext struct { Token string Renderer renderer.Renderer Organization string + Project string } // GetCLIContext transforms the untyped context passed to cobra commands into a CLIContext. @@ -87,6 +102,7 @@ func GetCLIContext(ctx context.Context) *CLIContext { Token: ctx.Value(koyeb.ContextAccessToken).(string), Renderer: ctx.Value(ctx_renderer).(renderer.Renderer), Organization: ctx.Value(ctx_organization).(string), + Project: ctx.Value(ctx_project).(string), } } diff --git a/pkg/koyeb/databases.go b/pkg/koyeb/databases.go index 54fdfa3a..f046ee3e 100644 --- a/pkg/koyeb/databases.go +++ b/pkg/koyeb/databases.go @@ -25,6 +25,7 @@ func NewDatabaseCmd() *cobra.Command { Short: "List databases", RunE: WithCLIContext(h.List), } + listDbCmd.Flags().StringP("project", "p", "", "Project name or ID") databaseCmd.AddCommand(listDbCmd) getDbCmd := &cobra.Command{ @@ -34,6 +35,7 @@ func NewDatabaseCmd() *cobra.Command { RunE: WithCLIContext(h.Get), } getDbCmd.Flags().String("app", "", "Database application. If the application does not exist, it will be created. Can also be provided in the database name with the format `app-name/database-name`") + getDbCmd.Flags().StringP("project", "p", "", "Project name or ID") databaseCmd.AddCommand(getDbCmd) createDbCmd := &cobra.Command{ @@ -58,6 +60,7 @@ func NewDatabaseCmd() *cobra.Command { }), } addCreateDbServiceDefinitionFlags(createDbCmd.Flags()) + createDbCmd.Flags().StringP("project", "p", "", "Project name or ID") databaseCmd.AddCommand(createDbCmd) updateDbCmd := &cobra.Command{ @@ -113,6 +116,7 @@ func NewDatabaseCmd() *cobra.Command { }), } addUpdateDbServiceDefinitionFlags(updateDbCmd.Flags()) + updateDbCmd.Flags().StringP("project", "p", "", "Project name or ID") databaseCmd.AddCommand(updateDbCmd) deleteDbCmd := &cobra.Command{ @@ -121,6 +125,7 @@ func NewDatabaseCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Delete), } + deleteDbCmd.Flags().StringP("project", "p", "", "Project name or ID") databaseCmd.AddCommand(deleteDbCmd) return databaseCmd diff --git a/pkg/koyeb/domains.go b/pkg/koyeb/domains.go index 5240e172..6e5669bd 100644 --- a/pkg/koyeb/domains.go +++ b/pkg/koyeb/domains.go @@ -19,6 +19,7 @@ func NewDomainCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Get), } + getDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(getDomainCmd) createDomainCmd := &cobra.Command{ @@ -28,6 +29,7 @@ func NewDomainCmd() *cobra.Command { RunE: WithCLIContext(h.Create), } createDomainCmd.Flags().String("attach-to", "", "Upon creation, assign to given app") + createDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(createDomainCmd) describeDomainCmd := &cobra.Command{ @@ -36,6 +38,7 @@ func NewDomainCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Describe), } + describeDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(describeDomainCmd) listDomainCmd := &cobra.Command{ @@ -43,6 +46,7 @@ func NewDomainCmd() *cobra.Command { Short: "List domains", RunE: WithCLIContext(h.List), } + listDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(listDomainCmd) deleteDomainCmd := &cobra.Command{ @@ -50,6 +54,7 @@ func NewDomainCmd() *cobra.Command { Short: "Delete domain", RunE: WithCLIContext(h.Delete), } + deleteDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(deleteDomainCmd) refreshDomainCmd := &cobra.Command{ @@ -58,6 +63,7 @@ func NewDomainCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Refresh), } + refreshDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(refreshDomainCmd) attachDomainCmd := &cobra.Command{ @@ -66,6 +72,7 @@ func NewDomainCmd() *cobra.Command { Args: cobra.ExactArgs(2), RunE: WithCLIContext(h.Attach), } + attachDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(attachDomainCmd) detachDomainCmd := &cobra.Command{ @@ -74,6 +81,7 @@ func NewDomainCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Detach), } + detachDomainCmd.Flags().StringP("project", "p", "", "Project name or ID") domainCmd.AddCommand(detachDomainCmd) return domainCmd diff --git a/pkg/koyeb/domains_create.go b/pkg/koyeb/domains_create.go index 16c09c64..b5b3cca5 100644 --- a/pkg/koyeb/domains_create.go +++ b/pkg/koyeb/domains_create.go @@ -13,6 +13,16 @@ func (h *DomainHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []strin createDomainReq.SetName(args[0]) createDomainReq.SetType(koyeb.DOMAINTYPE_CUSTOM) + projectFlag, _ := cmd.Flags().GetString("project") + if projectFlag != "" { + projectHandler := NewProjectHandler() + projectID, err := projectHandler.ResolveProjectArgs(ctx, projectFlag) + if err != nil { + return err + } + createDomainReq.SetProjectId(projectID) + } + attachToApp := GetStringFlags(cmd, "attach-to") if attachToApp != "" { appID, err := ctx.Mapper.App().ResolveID(attachToApp) diff --git a/pkg/koyeb/idmapper/idmapper.go b/pkg/koyeb/idmapper/idmapper.go index c4a953a9..51520701 100644 --- a/pkg/koyeb/idmapper/idmapper.go +++ b/pkg/koyeb/idmapper/idmapper.go @@ -18,6 +18,7 @@ type Mapper struct { database *DatabaseMapper volume *VolumeMapper snapshot *SnapshotMapper + project *ProjectMapper } func NewMapper(ctx context.Context, client *koyeb.APIClient) *Mapper { @@ -32,6 +33,7 @@ func NewMapper(ctx context.Context, client *koyeb.APIClient) *Mapper { databaseMapper := NewDatabaseMapper(ctx, client, appMapper) volumeMapper := NewVolumeMapper(ctx, client) snapshotMapper := NewSnapshotMapper(ctx, client) + projectMapper := NewProjectMapper(ctx, client) return &Mapper{ app: appMapper, @@ -45,6 +47,7 @@ func NewMapper(ctx context.Context, client *koyeb.APIClient) *Mapper { database: databaseMapper, volume: volumeMapper, snapshot: snapshotMapper, + project: projectMapper, } } @@ -91,3 +94,7 @@ func (mapper *Mapper) Volume() *VolumeMapper { func (mapper *Mapper) Snapshot() *SnapshotMapper { return mapper.snapshot } + +func (mapper *Mapper) Project() *ProjectMapper { + return mapper.project +} diff --git a/pkg/koyeb/idmapper/project.go b/pkg/koyeb/idmapper/project.go new file mode 100644 index 00000000..453fe418 --- /dev/null +++ b/pkg/koyeb/idmapper/project.go @@ -0,0 +1,107 @@ +package idmapper + +import ( + "context" + "strconv" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" +) + +type ProjectMapper struct { + ctx context.Context + client *koyeb.APIClient + fetched bool + sidMap *IDMap + nameMap *IDMap +} + +func NewProjectMapper(ctx context.Context, client *koyeb.APIClient) *ProjectMapper { + return &ProjectMapper{ + ctx: ctx, + client: client, + fetched: false, + sidMap: NewIDMap(), + nameMap: NewIDMap(), + } +} + +func (mapper *ProjectMapper) ResolveID(val string) (string, error) { + if IsUUIDv4(val) { + return val, nil + } + + if !mapper.fetched { + err := mapper.fetch() + if err != nil { + return "", err + } + } + + id, ok := mapper.sidMap.GetID(val) + if ok { + return id, nil + } + + id, ok = mapper.nameMap.GetID(val) + if ok { + return id, nil + } + return "", errors.NewCLIErrorForMapperResolve( + "project", + val, + []string{"project full UUID", "project short ID (8 characters)", "project name"}, + ) +} + +func (mapper *ProjectMapper) fetch() error { + radix := NewRadixTree() + + page := int64(0) + limit := int64(100) + for { + offset := page * limit + res, resp, err := mapper.client.ProjectsApi.ListProjects(mapper.ctx). + Limit(strconv.FormatInt(limit, 10)). + Offset(strconv.FormatInt(offset, 10)). + Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + "Error listing projects to resolve the provided identifier to an object ID", + err, + resp, + ) + } + + projects := res.GetProjects() + for i := range projects { + project := &projects[i] + radix.Insert(getKey(project.GetId()), project) + } + + if int64(len(projects)) < limit { + break + } + + page++ + } + + minLength := radix.MinimalLength(8) + err := radix.ForEach(func(key Key, value Value) error { + project := value.(*koyeb.Project) + id := project.GetId() + name := project.GetName() + sid := getShortID(id, minLength) + + mapper.sidMap.Set(id, sid) + mapper.nameMap.Set(id, name) + + return nil + }) + if err != nil { + return err + } + + mapper.fetched = true + return nil +} diff --git a/pkg/koyeb/koyeb.go b/pkg/koyeb/koyeb.go index a4726c22..0710d0e1 100644 --- a/pkg/koyeb/koyeb.go +++ b/pkg/koyeb/koyeb.go @@ -31,6 +31,7 @@ var ( debugFull bool debug bool organization string + project string loginCmd = &cobra.Command{ Use: "login", @@ -76,7 +77,7 @@ func GetRootCommand() *cobra.Command { return err } DetectUpdates() - return SetupCLIContext(cmd, organization) + return SetupCLIContext(cmd, organization, project) }, } @@ -91,18 +92,21 @@ func GetRootCommand() *cobra.Command { rootCmd.PersistentFlags().String("url", "https://app.koyeb.com", "url of the api") rootCmd.PersistentFlags().String("token", "", "API token") rootCmd.PersistentFlags().StringVar(&organization, "organization", "", "organization ID") + rootCmd.PersistentFlags().StringVarP(&project, "project", "p", "", "project ID") // viper.BindPFlag returns an error only if the second argument is nil, which is never the case here, so we ignore the error viper.BindPFlag("url", rootCmd.PersistentFlags().Lookup("url")) //nolint:errcheck viper.BindPFlag("token", rootCmd.PersistentFlags().Lookup("token")) //nolint:errcheck viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) //nolint:errcheck viper.BindPFlag("organization", rootCmd.PersistentFlags().Lookup("organization")) //nolint:errcheck + viper.BindPFlag("project", rootCmd.PersistentFlags().Lookup("project")) //nolint:errcheck rootCmd.AddCommand(loginCmd) rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(completionCmd) rootCmd.AddCommand(NewOrganizationCmd()) + rootCmd.AddCommand(NewProjectCmd()) rootCmd.AddCommand(NewSecretCmd()) rootCmd.AddCommand(NewAppCmd()) rootCmd.AddCommand(NewDomainCmd()) @@ -265,5 +269,6 @@ func initConfig(rootCmd *cobra.Command) error { token = viper.GetString("token") debug = viper.GetBool("debug") organization = viper.GetString("organization") + project = viper.GetString("project") return nil } diff --git a/pkg/koyeb/projects.go b/pkg/koyeb/projects.go new file mode 100644 index 00000000..70460fc4 --- /dev/null +++ b/pkg/koyeb/projects.go @@ -0,0 +1,139 @@ +package koyeb + +import ( + "fmt" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func NewProjectCmd() *cobra.Command { + h := NewProjectHandler() + + projectCmd := &cobra.Command{ + Use: "projects ACTION", + Aliases: []string{"project", "projs", "proj"}, + Short: "Projects", + } + + createProjectCmd := &cobra.Command{ + Use: "create NAME", + Short: "Create project", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(func(ctx *CLIContext, cmd *cobra.Command, args []string) error { + createProject := koyeb.NewCreateProjectWithDefaults() + SyncFlags(cmd, args, createProject) + return h.Create(ctx, cmd, args, createProject) + }), + } + createProjectCmd.Flags().String("description", "", "Project description") + projectCmd.AddCommand(createProjectCmd) + + getProjectCmd := &cobra.Command{ + Use: "get NAME", + Short: "Get project", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(h.Get), + } + projectCmd.AddCommand(getProjectCmd) + + listProjectCmd := &cobra.Command{ + Use: "list", + Short: "List projects", + RunE: WithCLIContext(h.List), + } + projectCmd.AddCommand(listProjectCmd) + + describeProjectCmd := &cobra.Command{ + Use: "describe NAME", + Short: "Describe project", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(h.Describe), + } + projectCmd.AddCommand(describeProjectCmd) + + updateProjectCmd := &cobra.Command{ + Use: "update NAME", + Short: "Update project", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(func(ctx *CLIContext, cmd *cobra.Command, args []string) error { + updateProject := koyeb.NewProjectWithDefaults() + SyncFlags(cmd, args, updateProject) + return h.Update(ctx, cmd, args, updateProject) + }), + } + updateProjectCmd.Flags().StringP("name", "n", "", "Project name") + updateProjectCmd.Flags().String("description", "", "Project description") + projectCmd.AddCommand(updateProjectCmd) + + deleteProjectCmd := &cobra.Command{ + Use: "delete NAME", + Short: "Delete project", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(h.Delete), + } + projectCmd.AddCommand(deleteProjectCmd) + + switchProjectCmd := &cobra.Command{ + Use: "switch NAME", + Short: "Switch the default project for the CLI context", + Args: cobra.ExactArgs(1), + RunE: WithCLIContext(h.Switch), + } + projectCmd.AddCommand(switchProjectCmd) + + return projectCmd +} + +func NewProjectHandler() *ProjectHandler { + return &ProjectHandler{} +} + +type ProjectHandler struct { +} + +func (h *ProjectHandler) ResolveProjectArgs(ctx *CLIContext, val string) (string, error) { + projectMapper := ctx.Mapper.Project() + id, err := projectMapper.ResolveID(val) + if err != nil { + return "", err + } + return id, nil +} + +func (h *ProjectHandler) CreateProject(ctx *CLIContext, payload *koyeb.CreateProject) (*koyeb.CreateProjectReply, error) { + res, resp, err := ctx.Client.ProjectsApi.CreateProject(ctx.Context).Project(*payload).Execute() + if err != nil { + return nil, errors.NewCLIErrorFromAPIError( + fmt.Sprintf("Error while creating the project `%s`", payload.GetName()), + err, + resp, + ) + } + return res, nil +} + +func (h *ProjectHandler) Switch(ctx *CLIContext, cmd *cobra.Command, args []string) error { + project, err := h.ResolveProjectArgs(ctx, args[0]) + if err != nil { + return err + } + viper.Set("project", project) + if err := viper.WriteConfig(); err != nil { + return &errors.CLIError{ + What: "Unable to switch the current project", + Why: "we were unable to write the configuration file", + Additional: []string{ + "The command `koyeb project switch` needs to update your configuration file, usually located in $HOME/.koyeb.yaml", + "If you do not have write access to this file, you can use the --config flag to specify a different location.", + "Alternatively, you can manually edit the configuration file and set the project field to the project ID you want to use.", + "You can also provide the project UUID with the --project flag.", + }, + Orig: err, + Solution: "Fix the issue preventing the CLI to write the configuration file, or manually edit the configuration file", + } + } + return nil +} diff --git a/pkg/koyeb/projects_create.go b/pkg/koyeb/projects_create.go new file mode 100644 index 00000000..99d5f5fb --- /dev/null +++ b/pkg/koyeb/projects_create.go @@ -0,0 +1,20 @@ +package koyeb + +import ( + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []string, createProject *koyeb.CreateProject) error { + createProject.SetName(args[0]) + + res, err := h.CreateProject(ctx, createProject) + if err != nil { + return err + } + + full := GetBoolFlags(cmd, "full") + getProjectsReply := NewGetProjectReply(ctx.Mapper, &koyeb.GetProjectReply{Project: res.Project}, full) + ctx.Renderer.Render(getProjectsReply) + return nil +} diff --git a/pkg/koyeb/projects_delete.go b/pkg/koyeb/projects_delete.go new file mode 100644 index 00000000..1abfa2d9 --- /dev/null +++ b/pkg/koyeb/projects_delete.go @@ -0,0 +1,28 @@ +package koyeb + +import ( + "fmt" + + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) Delete(ctx *CLIContext, cmd *cobra.Command, args []string) error { + project, err := h.ResolveProjectArgs(ctx, args[0]) + if err != nil { + return err + } + + _, resp, err := ctx.Client.ProjectsApi.DeleteProject(ctx.Context, project).Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + fmt.Sprintf("Error while deleting the project `%s`", args[0]), + err, + resp, + ) + } + + log.Infof("Project %s deleted.", args[0]) + return nil +} diff --git a/pkg/koyeb/projects_describe.go b/pkg/koyeb/projects_describe.go new file mode 100644 index 00000000..8ed4a620 --- /dev/null +++ b/pkg/koyeb/projects_describe.go @@ -0,0 +1,72 @@ +package koyeb + +import ( + "fmt" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + "github.com/koyeb/koyeb-cli/pkg/koyeb/idmapper" + "github.com/koyeb/koyeb-cli/pkg/koyeb/renderer" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) Describe(ctx *CLIContext, cmd *cobra.Command, args []string) error { + project, err := h.ResolveProjectArgs(ctx, args[0]) + if err != nil { + return err + } + + res, resp, err := ctx.Client.ProjectsApi.GetProject(ctx.Context, project).Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + fmt.Sprintf("Error while retrieving the project `%s`", args[0]), + err, + resp, + ) + } + + full := GetBoolFlags(cmd, "full") + describeProjectsReply := NewDescribeProjectReply(ctx.Mapper, res, full) + ctx.Renderer.Render(describeProjectsReply) + return nil +} + +type DescribeProjectReply struct { + mapper *idmapper.Mapper + value *koyeb.GetProjectReply + full bool +} + +func NewDescribeProjectReply(mapper *idmapper.Mapper, value *koyeb.GetProjectReply, full bool) *DescribeProjectReply { + return &DescribeProjectReply{ + mapper: mapper, + value: value, + full: full, + } +} + +func (DescribeProjectReply) Title() string { + return "Project" +} + +func (r *DescribeProjectReply) MarshalBinary() ([]byte, error) { + return r.value.GetProject().MarshalJSON() +} + +func (r *DescribeProjectReply) Headers() []string { + return []string{"id", "name", "description", "created_at", "updated_at"} +} + +func (r *DescribeProjectReply) Fields() []map[string]string { + item := r.value.GetProject() + fields := map[string]string{ + "id": renderer.FormatID(item.GetId(), r.full), + "name": item.GetName(), + "description": item.GetDescription(), + "created_at": renderer.FormatTime(item.GetCreatedAt()), + "updated_at": renderer.FormatTime(item.GetUpdatedAt()), + } + + resp := []map[string]string{fields} + return resp +} diff --git a/pkg/koyeb/projects_get.go b/pkg/koyeb/projects_get.go new file mode 100644 index 00000000..af14289a --- /dev/null +++ b/pkg/koyeb/projects_get.go @@ -0,0 +1,71 @@ +package koyeb + +import ( + "fmt" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + "github.com/koyeb/koyeb-cli/pkg/koyeb/idmapper" + "github.com/koyeb/koyeb-cli/pkg/koyeb/renderer" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) Get(ctx *CLIContext, cmd *cobra.Command, args []string) error { + project, err := h.ResolveProjectArgs(ctx, args[0]) + if err != nil { + return err + } + + res, resp, err := ctx.Client.ProjectsApi.GetProject(ctx.Context, project).Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + fmt.Sprintf("Error while retrieving the project `%s`", args[0]), + err, + resp, + ) + } + + full := GetBoolFlags(cmd, "full") + getProjectsReply := NewGetProjectReply(ctx.Mapper, res, full) + ctx.Renderer.Render(getProjectsReply) + return nil +} + +type GetProjectReply struct { + mapper *idmapper.Mapper + value *koyeb.GetProjectReply + full bool +} + +func NewGetProjectReply(mapper *idmapper.Mapper, value *koyeb.GetProjectReply, full bool) *GetProjectReply { + return &GetProjectReply{ + mapper: mapper, + value: value, + full: full, + } +} + +func (GetProjectReply) Title() string { + return "Project" +} + +func (r *GetProjectReply) MarshalBinary() ([]byte, error) { + return r.value.GetProject().MarshalJSON() +} + +func (r *GetProjectReply) Headers() []string { + return []string{"id", "name", "description", "created_at"} +} + +func (r *GetProjectReply) Fields() []map[string]string { + item := r.value.GetProject() + fields := map[string]string{ + "id": renderer.FormatID(item.GetId(), r.full), + "name": item.GetName(), + "description": item.GetDescription(), + "created_at": renderer.FormatTime(item.GetCreatedAt()), + } + + resp := []map[string]string{fields} + return resp +} diff --git a/pkg/koyeb/projects_list.go b/pkg/koyeb/projects_list.go new file mode 100644 index 00000000..54da7325 --- /dev/null +++ b/pkg/koyeb/projects_list.go @@ -0,0 +1,87 @@ +package koyeb + +import ( + "strconv" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + "github.com/koyeb/koyeb-cli/pkg/koyeb/idmapper" + "github.com/koyeb/koyeb-cli/pkg/koyeb/renderer" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) List(ctx *CLIContext, cmd *cobra.Command, args []string) error { + list := []koyeb.Project{} + + page := int64(0) + offset := int64(0) + limit := int64(100) + for { + res, resp, err := ctx.Client.ProjectsApi.ListProjects(ctx.Context). + Limit(strconv.FormatInt(limit, 10)).Offset(strconv.FormatInt(offset, 10)).Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + "Error while listing the projects", + err, + resp, + ) + } + list = append(list, res.GetProjects()...) + + // If we got fewer items than requested, we've reached the end + if int64(len(res.GetProjects())) < limit { + break + } + + page++ + offset = page * limit + } + + full := GetBoolFlags(cmd, "full") + listProjectsReply := NewListProjectsReply(ctx.Mapper, &koyeb.ListProjectsReply{Projects: list}, full) + ctx.Renderer.Render(listProjectsReply) + return nil +} + +type ListProjectsReply struct { + mapper *idmapper.Mapper + value *koyeb.ListProjectsReply + full bool +} + +func NewListProjectsReply(mapper *idmapper.Mapper, value *koyeb.ListProjectsReply, full bool) *ListProjectsReply { + return &ListProjectsReply{ + mapper: mapper, + value: value, + full: full, + } +} + +func (ListProjectsReply) Title() string { + return "Projects" +} + +func (r *ListProjectsReply) MarshalBinary() ([]byte, error) { + return r.value.MarshalJSON() +} + +func (r *ListProjectsReply) Headers() []string { + return []string{"id", "name", "description", "created_at"} +} + +func (r *ListProjectsReply) Fields() []map[string]string { + items := r.value.GetProjects() + resp := make([]map[string]string, 0, len(items)) + + for _, item := range items { + fields := map[string]string{ + "id": renderer.FormatID(item.GetId(), r.full), + "name": item.GetName(), + "description": item.GetDescription(), + "created_at": renderer.FormatTime(item.GetCreatedAt()), + } + resp = append(resp, fields) + } + + return resp +} diff --git a/pkg/koyeb/projects_update.go b/pkg/koyeb/projects_update.go new file mode 100644 index 00000000..80249efb --- /dev/null +++ b/pkg/koyeb/projects_update.go @@ -0,0 +1,30 @@ +package koyeb + +import ( + "fmt" + + "github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" + "github.com/koyeb/koyeb-cli/pkg/koyeb/errors" + "github.com/spf13/cobra" +) + +func (h *ProjectHandler) Update(ctx *CLIContext, cmd *cobra.Command, args []string, updateProject *koyeb.Project) error { + project, err := h.ResolveProjectArgs(ctx, args[0]) + if err != nil { + return err + } + + res, resp, err := ctx.Client.ProjectsApi.UpdateProject(ctx.Context, project).Project(*updateProject).Execute() + if err != nil { + return errors.NewCLIErrorFromAPIError( + fmt.Sprintf("Error while updating the project `%s`", args[0]), + err, + resp, + ) + } + + full := GetBoolFlags(cmd, "full") + getProjectsReply := NewGetProjectReply(ctx.Mapper, &koyeb.GetProjectReply{Project: res.Project}, full) + ctx.Renderer.Render(getProjectsReply) + return nil +} diff --git a/pkg/koyeb/secrets.go b/pkg/koyeb/secrets.go index bf3b6938..a01820da 100644 --- a/pkg/koyeb/secrets.go +++ b/pkg/koyeb/secrets.go @@ -132,6 +132,7 @@ func NewSecretCmd() *cobra.Command { } createSecretCmd.Flags().Var(&flagSecretType, "type", fmt.Sprintf("Secret type (%s)", strings.Join(SecretTypeAllValues(), ", "))) addSecretFlags(createSecretCmd.Flags(), &flagSecretType) + createSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(createSecretCmd) getSecretCmd := &cobra.Command{ @@ -140,6 +141,7 @@ func NewSecretCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Get), } + getSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(getSecretCmd) listSecretCmd := &cobra.Command{ @@ -147,6 +149,7 @@ func NewSecretCmd() *cobra.Command { Short: "List secrets", RunE: WithCLIContext(h.List), } + listSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(listSecretCmd) describeSecretCmd := &cobra.Command{ @@ -155,6 +158,7 @@ func NewSecretCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Describe), } + describeSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(describeSecretCmd) updateSecretCmd := &cobra.Command{ @@ -208,6 +212,7 @@ func NewSecretCmd() *cobra.Command { }), } addSecretFlags(updateSecretCmd.Flags(), &flagSecretType) + updateSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(updateSecretCmd) deleteSecretCmd := &cobra.Command{ @@ -216,6 +221,7 @@ func NewSecretCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Delete), } + deleteSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(deleteSecretCmd) revealSecretCmd := &cobra.Command{ @@ -225,6 +231,7 @@ func NewSecretCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: WithCLIContext(h.Reveal), } + revealSecretCmd.Flags().StringP("project", "p", "", "Project name or ID") secretCmd.AddCommand(revealSecretCmd) return secretCmd diff --git a/pkg/koyeb/secrets_create.go b/pkg/koyeb/secrets_create.go index 99f57206..0cfebe54 100644 --- a/pkg/koyeb/secrets_create.go +++ b/pkg/koyeb/secrets_create.go @@ -9,6 +9,16 @@ import ( ) func (h *SecretHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []string, createSecret *koyeb.CreateSecret) error { + projectFlag, _ := cmd.Flags().GetString("project") + if projectFlag != "" { + projectHandler := NewProjectHandler() + projectID, err := projectHandler.ResolveProjectArgs(ctx, projectFlag) + if err != nil { + return err + } + createSecret.SetProjectId(projectID) + } + res, resp, err := ctx.Client.SecretsApi.CreateSecret(ctx.Context).Secret(*createSecret).Execute() if err != nil { return errors.NewCLIErrorFromAPIError( diff --git a/pkg/koyeb/services.go b/pkg/koyeb/services.go index 03578e01..6505ec8b 100644 --- a/pkg/koyeb/services.go +++ b/pkg/koyeb/services.go @@ -72,6 +72,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp } h.addServiceDefinitionFlags(createServiceCmd.Flags()) createServiceCmd.Flags().StringP("app", "a", "", "Service application") + createServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") createServiceCmd.Flags().Bool("wait", false, "Waits until service deployment is done") createServiceCmd.Flags().Duration("wait-timeout", 5*time.Minute, "Duration the wait will last until timeout") serviceCmd.AddCommand(createServiceCmd) @@ -83,6 +84,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp RunE: WithCLIContext(h.Get), } getServiceCmd.Flags().StringP("app", "a", "", "Service application") + getServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(getServiceCmd) unappliedChangesCmd := &cobra.Command{ @@ -123,6 +125,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp } serviceCmd.AddCommand(listServiceCmd) listServiceCmd.Flags().StringP("app", "a", "", "App") + listServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") listServiceCmd.Flags().StringP("name", "n", "", "Service name") describeServiceCmd := &cobra.Command{ @@ -132,6 +135,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp RunE: WithCLIContext(h.Describe), } describeServiceCmd.Flags().StringP("app", "a", "", "Service application") + describeServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(describeServiceCmd) execServiceCmd := &cobra.Command{ @@ -142,6 +146,7 @@ $> koyeb service create myservice --app myapp --docker nginx --port 80:tcp RunE: WithCLIContext(h.Exec), } execServiceCmd.Flags().StringP("app", "a", "", "Service application") + execServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(execServiceCmd) updateServiceCmd := &cobra.Command{ @@ -253,6 +258,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' } h.addServiceDefinitionFlags(updateServiceCmd.Flags()) updateServiceCmd.Flags().StringP("app", "a", "", "Service application") + updateServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") updateServiceCmd.Flags().String("name", "", "Specify to update the service name") updateServiceCmd.Flags().Bool("override", false, "Override the service configuration with the new configuration instead of merging them") updateServiceCmd.Flags().Bool("skip-build", false, "If there has been at least one past successfully build deployment, use the last one instead of rebuilding. WARNING: this can lead to unexpected behavior if the build depends, for example, on environment variables.") @@ -268,6 +274,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' RunE: WithCLIContext(h.ReDeploy), } redeployServiceCmd.Flags().StringP("app", "a", "", "Service application") + redeployServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") redeployServiceCmd.Flags().Bool("skip-build", false, "If there has been at least one past successfully build deployment, use the last one instead of rebuilding. WARNING: this can lead to unexpected behavior if the build depends, for example, on environment variables.") redeployServiceCmd.Flags().Bool("wait", false, "Waits until service deployment is done.") redeployServiceCmd.Flags().Duration("wait-timeout", 5*time.Minute, "Duration the wait will last until timeout") @@ -281,6 +288,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' RunE: WithCLIContext(h.Delete), } deleteServiceCmd.Flags().StringP("app", "a", "", "Service application") + deleteServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(deleteServiceCmd) pauseServiceCmd := &cobra.Command{ @@ -290,6 +298,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' RunE: WithCLIContext(h.Pause), } pauseServiceCmd.Flags().StringP("app", "a", "", "Service application") + pauseServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(pauseServiceCmd) resumeServiceCmd := &cobra.Command{ @@ -299,6 +308,7 @@ $> koyeb service update myapp/myservice --port 80:tcp --route '!/' RunE: WithCLIContext(h.Resume), } resumeServiceCmd.Flags().StringP("app", "a", "", "Service application") + resumeServiceCmd.Flags().StringP("project", "p", "", "Project name or ID") serviceCmd.AddCommand(resumeServiceCmd) scaleCmd := &cobra.Command{ diff --git a/pkg/koyeb/services_create.go b/pkg/koyeb/services_create.go index cad32c80..fe4e1c75 100644 --- a/pkg/koyeb/services_create.go +++ b/pkg/koyeb/services_create.go @@ -17,6 +17,16 @@ func (h *ServiceHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []stri return err } + projectFlag, _ := cmd.Flags().GetString("project") + if projectFlag != "" { + projectHandler := NewProjectHandler() + projectID, err := projectHandler.ResolveProjectArgs(ctx, projectFlag) + if err != nil { + return err + } + createService.SetProjectId(projectID) + } + app, err := h.ResolveAppArgs(ctx, appID) if err != nil { return err diff --git a/pkg/koyeb/volumes.go b/pkg/koyeb/volumes.go index 2a914d30..f9c0bf48 100644 --- a/pkg/koyeb/volumes.go +++ b/pkg/koyeb/volumes.go @@ -100,6 +100,7 @@ func NewVolumeCmd() *cobra.Command { createVolumeCmd.Flags().Int64("size", -1, "Size of the volume in GB") createVolumeCmd.Flags().Bool("read-only", false, "Force the volume to be read-only") createVolumeCmd.Flags().String("snapshot", "", "Specify a snapshot to use to create the volume from") + createVolumeCmd.Flags().StringP("project", "p", "", "Project name or ID") volumeCmd.AddCommand(createVolumeCmd) getVolumeCmd := &cobra.Command{ @@ -110,6 +111,7 @@ func NewVolumeCmd() *cobra.Command { return h.Get(ctx, cmd, args) }), } + getVolumeCmd.Flags().StringP("project", "p", "", "Project name or ID") volumeCmd.AddCommand(getVolumeCmd) listVolumeCmd := &cobra.Command{ @@ -119,6 +121,7 @@ func NewVolumeCmd() *cobra.Command { return h.List(ctx, cmd, args) }), } + listVolumeCmd.Flags().StringP("project", "p", "", "Project name or ID") volumeCmd.AddCommand(listVolumeCmd) updateVolumeCmd := &cobra.Command{ @@ -146,6 +149,7 @@ func NewVolumeCmd() *cobra.Command { } updateVolumeCmd.Flags().String("name", "", "Change the volume name") updateVolumeCmd.Flags().Int64("size", -1, "Increase the volume size") + updateVolumeCmd.Flags().StringP("project", "p", "", "Project name or ID") volumeCmd.AddCommand(updateVolumeCmd) deleteVolumeCmd := &cobra.Command{ @@ -156,6 +160,7 @@ func NewVolumeCmd() *cobra.Command { return h.Delete(ctx, cmd, args) }), } + deleteVolumeCmd.Flags().StringP("project", "p", "", "Project name or ID") volumeCmd.AddCommand(deleteVolumeCmd) return volumeCmd diff --git a/pkg/koyeb/volumes_create.go b/pkg/koyeb/volumes_create.go index 29c0cc57..cdacd836 100644 --- a/pkg/koyeb/volumes_create.go +++ b/pkg/koyeb/volumes_create.go @@ -9,6 +9,16 @@ import ( ) func (h *VolumeHandler) Create(ctx *CLIContext, cmd *cobra.Command, args []string, createVolume *koyeb.CreatePersistentVolumeRequest) error { + projectFlag, _ := cmd.Flags().GetString("project") + if projectFlag != "" { + projectHandler := NewProjectHandler() + projectID, err := projectHandler.ResolveProjectArgs(ctx, projectFlag) + if err != nil { + return err + } + createVolume.SetProjectId(projectID) + } + res, resp, err := ctx.Client.PersistentVolumesApi.CreatePersistentVolume(ctx.Context).Body(*createVolume).Execute() if err != nil { return errors.NewCLIErrorFromAPIError(