diff --git a/.github/workflows/deploy_prod.yml b/.github/workflows/deploy_prod.yml index ef453a67..4a201a83 100644 --- a/.github/workflows/deploy_prod.yml +++ b/.github/workflows/deploy_prod.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest container: - image: golang:1.25.3 + image: golang:1.25.5 outputs: binver: ${{ steps.binversion.outputs.BIN_VERSION }} env: diff --git a/.github/workflows/deploy_stage.yml b/.github/workflows/deploy_stage.yml index 9e1dded0..2174810f 100644 --- a/.github/workflows/deploy_stage.yml +++ b/.github/workflows/deploy_stage.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest container: - image: golang:1.25.3 + image: golang:1.25.5 env: CGO_ENABLED: 0 # Statically linked diff --git a/.github/workflows/e2e_test.yml b/.github/workflows/e2e_test.yml index 4f5c497d..0146272a 100644 --- a/.github/workflows/e2e_test.yml +++ b/.github/workflows/e2e_test.yml @@ -14,11 +14,47 @@ jobs: runs-on: ubuntu-latest if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-e2e') }} container: - image: golang:1.25.3 + image: golang:1.25.5 env: CGO_ENABLED: 0 # Statically linked steps: + - name: Check for E2E_TOKEN secret + id: check_secret + run: | + if [ -z "${{ secrets.E2E_TOKEN }}" ]; then + echo "secret_exists=false" >> $GITHUB_OUTPUT + else + echo "secret_exists=true" >> $GITHUB_OUTPUT + fi + + - name: Add skip-e2e label if E2E_TOKEN is missing + if: steps.check_secret.outputs.secret_exists == 'false' + uses: actions-ecosystem/action-add-labels@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: | + skip-e2e + + - name: Comment on PR if E2E_TOKEN is missing + if: steps.check_secret.outputs.secret_exists == 'false' + uses: peter-evans/create-or-update-comment@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + body: | + ⚠️ **E2E Tests Skipped** + + The E2E tests were skipped because the `E2E_TOKEN` secret is not configured in this repository. The `skip-e2e` label has been added to prevent future E2E test runs on this PR. + + To enable E2E tests, please: + 1. Go to your repository Settings → Secrets and variables → Actions + 2. Add a new repository secret named `E2E_TOKEN` + 3. Set the value to a valid Azion personal token + 4. Remove the `skip-e2e` label from this PR + + Once configured, the E2E tests will run automatically on future pull requests. + - name: Setting GIT run: git config --global url."https://${{ secrets.GLOBAL_TOKEN }}:x-oauth-basic@github.com/aziontech".insteadOf "https://github.com/aziontech" diff --git a/.github/workflows/generate_docs.yml b/.github/workflows/generate_docs.yml index 56afa1fc..dcd6c6cf 100644 --- a/.github/workflows/generate_docs.yml +++ b/.github/workflows/generate_docs.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest container: - image: golang:1.25.3 + image: golang:1.25.5 env: CGO_ENABLED: 0 # Statically linked diff --git a/.github/workflows/package-audit.yaml b/.github/workflows/package-audit.yaml index a18807b4..de7681c3 100644 --- a/.github/workflows/package-audit.yaml +++ b/.github/workflows/package-audit.yaml @@ -2,7 +2,7 @@ name: Package Auditing on: pull_request: - types: [opened, synchronize] + types: [opened, synchronize, labeled, unlabeled] jobs: PackageAuditing: @@ -11,8 +11,9 @@ jobs: pull-requests: write name: Package Auditor (GoVulnCheck) runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-vulncheck') }} container: - image: golang:1.25.3 + image: golang:1.25.5 steps: - name: Checkout Repository uses: actions/checkout@v3 @@ -20,5 +21,8 @@ jobs: - name: bypass dep run: go env -w GOPRIVATE=github.com/zRedShift/mimemagic + - name: Download dependencies + run: go mod download + - name: GoVulnCheck run: make govulncheck diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml index c61f4ab5..de294f93 100644 --- a/.github/workflows/test_and_build.yml +++ b/.github/workflows/test_and_build.yml @@ -14,7 +14,7 @@ jobs: if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-unit-tests') }} runs-on: ubuntu-latest container: - image: golang:1.25.3 + image: golang:1.25.5 steps: - name: Setting GIT diff --git a/Dockerfile b/Dockerfile index 30c57d06..66bc2849 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.25.3 +FROM golang:1.25.5 WORKDIR /go/src/app diff --git a/Makefile b/Makefile index b36b3b11..d88e1847 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ SHELL := env PATH=$(PATH) /bin/bash NAME := azion ifeq (, $(GO)) -$(error "No go binary found in your system, please install go 1.25.3 before continuing") +$(error "No go binary found in your system, please install go 1.25.5 before continuing") endif GOPATH ?= $(shell $(GO) env GOPATH) diff --git a/azion b/azion new file mode 100755 index 00000000..4589dbcf Binary files /dev/null and b/azion differ diff --git a/doc/DEPLOY-FLOWCHART.md b/doc/DEPLOY-FLOWCHART.md new file mode 100644 index 00000000..68ebf905 --- /dev/null +++ b/doc/DEPLOY-FLOWCHART.md @@ -0,0 +1,40 @@ +```mermaid +flowchart TB + + %% Triggers + A[Push to main] --> B[build job] + A2[Manual workflow_dispatch] --> B + + %% Build job + subgraph Build_Job["Job: build"] + direction TB + B1[Configure Git with GLOBAL_TOKEN] --> B2[Checkout code] + B2 --> B3[Mark repo as safe directory] + B3 --> B4[Bump version & create tag] + B4 --> B5[git fetch --tags] + B5 --> B6["Build binaries (make build, prod env)"] + B6 --> B7["Cross-build binaries (make cross-build, prod env)"] + B7 --> B8[Install AWS CLI] + B8 --> B9[Configure AWS creds for azion-downloads S3] + B9 --> B10["Upload built binaries to S3 (azion-downloads)"] + B10 --> B11[Compute BIN_VERSION from git tag] + B11 --> B12[Export BIN_VERSION to env/output] + B12 --> B13[Run GoReleaser release --clean] + end + + %% Downstream jobs + B --> C[Publish Packages] + + %% Chocolatey job + subgraph Choco_Job["Publish Chocolatey"] + direction TB + C1[Checkout code] --> C2[Install Chocolatey] + C2 --> C3["Update azion.nuspec version with BIN_VERSION"] + C3 --> C4["Download Windows binary from downloads.azion.com"] + C4 --> C5[Compute SHA256 checksum of azion.exe] + C5 --> C6["Replace {{CHECKSUM}} in chocolateyinstall.ps1"] + C6 --> C7["choco pack azion.nuspec"] + C7 --> C8["choco push azion..nupkg to push.chocolatey.org"] + end + +``` diff --git a/go.mod b/go.mod index 251a49f1..19fee248 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/aziontech/azion-cli -go 1.25.3 +go 1.25.5 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 - github.com/aws/aws-sdk-go-v2 v1.39.5 - github.com/aws/aws-sdk-go-v2/config v1.31.16 - github.com/aws/aws-sdk-go-v2/credentials v1.18.20 - github.com/aws/aws-sdk-go-v2/service/s3 v1.89.1 + github.com/aws/aws-sdk-go-v2 v1.40.1 + github.com/aws/aws-sdk-go-v2/config v1.32.3 + github.com/aws/aws-sdk-go-v2/credentials v1.19.3 + github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0 github.com/aziontech/azionapi-go-sdk v0.143.0 github.com/aziontech/go-thoth v0.0.0-20240228144710-d061a88cc39f github.com/aziontech/tablecli v0.0.0-20241007135202-07712c07aa9e @@ -40,6 +40,7 @@ require ( require github.com/aziontech/azionapi-v4-go-sdk-dev v0.84.0 require ( + github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect ) @@ -48,20 +49,20 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.2 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.12 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.12 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 // indirect - github.com/aws/smithy-go v1.23.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cloudflare/circl v1.6.1 // indirect @@ -100,8 +101,8 @@ require ( github.com/tidwall/sjson v1.2.5 github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.44.0 // indirect - golang.org/x/net v0.46.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect diff --git a/go.sum b/go.sum index 5a2b8e5d..6a8f731f 100644 --- a/go.sum +++ b/go.sum @@ -50,42 +50,44 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.39.5 h1:e/SXuia3rkFtapghJROrydtQpfQaaUgd1cUvyO1mp2w= -github.com/aws/aws-sdk-go-v2 v1.39.5/go.mod h1:yWSxrnioGUZ4WVv9TgMrNUeLV3PFESn/v+6T/Su8gnM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.2 h1:t9yYsydLYNBk9cJ73rgPhPWqOh/52fcWDQB5b1JsKSY= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.2/go.mod h1:IusfVNTmiSN3t4rhxWFaBAqn+mcNdwKtPcV16eYdgko= -github.com/aws/aws-sdk-go-v2/config v1.31.16 h1:E4Tz+tJiPc7kGnXwIfCyUj6xHJNpENlY11oKpRTgsjc= -github.com/aws/aws-sdk-go-v2/config v1.31.16/go.mod h1:2S9hBElpCyGMifv14WxQ7EfPumgoeCPZUpuPX8VtW34= -github.com/aws/aws-sdk-go-v2/credentials v1.18.20 h1:KFndAnHd9NUuzikHjQ8D5CfFVO+bgELkmcGY8yAw98Q= -github.com/aws/aws-sdk-go-v2/credentials v1.18.20/go.mod h1:9mCi28a+fmBHSQ0UM79omkz6JtN+PEsvLrnG36uoUv0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 h1:VO3FIM2TDbm0kqp6sFNR0PbioXJb/HzCDW6NtIZpIWE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12/go.mod h1:6C39gB8kg82tx3r72muZSrNhHia9rjGkX7ORaS2GKNE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12 h1:p/9flfXdoAnwJnuW9xHEAFY22R3A6skYkW19JFF9F+8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12/go.mod h1:ZTLHakoVCTtW8AaLGSwJ3LXqHD9uQKnOcv1TrpO6u2k= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12 h1:2lTWFvRcnWFFLzHWmtddu5MTchc5Oj2OOey++99tPZ0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12/go.mod h1:hI92pK+ho8HVcWMHKHrK3Uml4pfG7wvL86FzO0LVtQQ= +github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= +github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= +github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= +github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.12 h1:itu4KHu8JK/N6NcLIISlf3LL1LccMqruLUXZ9y7yBZw= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.12/go.mod h1:i+6vTU3xziikTY3vcox23X8pPGW5X3wVgd1VZ7ha+x8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2 h1:xtuxji5CS0JknaXoACOunXOYOQzgfTvGAc9s2QdCJA4= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2/go.mod h1:zxwi0DIR0rcRcgdbl7E2MSOvxDyyXGBlScvBkARFaLQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.3 h1:NEe7FaViguRQEm8zl8Ay/kC/QRsMtWUiCGZajQIsLdc= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.3/go.mod h1:JLuCKu5VfiLBBBl/5IzZILU7rxS0koQpHzMOCzycOJU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12 h1:MM8imH7NZ0ovIVX7D2RxfMDv7Jt9OiUXkcQ+GqywA7M= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12/go.mod h1:gf4OGwdNkbEsb7elw2Sy76odfhwNktWII3WgvQgQQ6w= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.12 h1:R3uW0iKl8rgNEXNjVGliW/oMEh9fO/LlUEV8RvIFr1I= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.12/go.mod h1:XEttbEr5yqsw8ebi7vlDoGJJjMXRez4/s9pibpJyL5s= -github.com/aws/aws-sdk-go-v2/service/s3 v1.89.1 h1:Dq82AV+Qxpno/fG162eAhnD8d48t9S+GZCfz7yv1VeA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.89.1/go.mod h1:MbKLznDKpf7PnSonNRUVYZzfP0CeLkRIUexeblgKcU4= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 h1:xHXvxst78wBpJFgDW07xllOx0IAzbryrSdM4nMVQ4Dw= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.0/go.mod h1:/e8m+AO6HNPPqMyfKRtzZ9+mBF5/x1Wk8QiDva4m07I= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 h1:tBw2Qhf0kj4ZwtsVpDiVRU3zKLvjvjgIjHMKirxXg8M= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4/go.mod h1:Deq4B7sRM6Awq/xyOBlxBdgW8/Z926KYNNaGMW2lrkA= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 h1:C+BRMnasSYFcgDw8o9H5hzehKzXyAb9GY5v/8bP9DUY= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.0/go.mod h1:4EjU+4mIx6+JqKQkruye+CaigV7alL3thVPfDd9VlMs= -github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M= -github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15 h1:NLYTEyZmVZo0Qh183sC8nC+ydJXOOeIL/qI/sS3PdLY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15/go.mod h1:Z803iB3B0bc8oJV8zH2PERLRfQUJ2n2BXISpsA4+O1M= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6 h1:P1MU/SuhadGvg2jtviDXPEejU3jBNhoeeAlRadHzvHI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6/go.mod h1:5KYaMG6wmVKMFBSfWoyG/zH8pWwzQFnKgpoSRlXHKdQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15 h1:wsSQ4SVz5YE1crz0Ap7VBZrV4nNqZt4CIBBT8mnwoNc= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15/go.mod h1:I7sditnFGtYMIqPRU1QoHZAUrXkGp4SczmlLwrNPlD0= +github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0 h1:IrbE3B8O9pm3lsg96AXIN5MXX4pECEuExh/A0Du3AuI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0/go.mod h1:/sJLzHtiiZvs6C1RbxS/anSAFwZD6oC6M/kotQzOiLw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aziontech/azionapi-go-sdk v0.143.0 h1:4eEBlYT10prgeCVTNR9FIc7f59Crbl2zrH1a4D1BUqU= github.com/aziontech/azionapi-go-sdk v0.143.0/go.mod h1:cA5DY/VP4X5Eu11LpQNzNn83ziKjja7QVMIl4J45feA= github.com/aziontech/azionapi-v4-go-sdk-dev v0.84.0 h1:WMBXtpqR+4LFzn1B05V4RmMIC/eF9OVbVp3OU/c45Wc= @@ -345,8 +347,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -410,8 +412,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/messages/deploy-remote/messages.go b/messages/deploy-remote/messages.go index 4abc3596..9269c3b5 100644 --- a/messages/deploy-remote/messages.go +++ b/messages/deploy-remote/messages.go @@ -47,6 +47,7 @@ var ( Create a Cache Settings configuration with the above specifications? (y/N)` SkipBucket = "Your project does not contain a '.edge/storage' folder. Skipping creation of bucket" SkipUpload = "Your project does not contain a '.edge/storage' folder. Skipping upload of static files" + SkipUploadBuild = "Skipping upload of static files due to project not being built and no new static files being generated" NameInUseBucket = "Bucket name is already in use. Trying to create bucket with the following name: %s\n" NameInUseApplication = "Application name is already in use. Trying to create Application with the following name: %s\n" NameInUseDomain = "Domain name is already in use. Trying to create Domain with the following name: %s\n" diff --git a/messages/dev/messages.go b/messages/dev/messages.go index f39e8bde..49c469cb 100644 --- a/messages/dev/messages.go +++ b/messages/dev/messages.go @@ -7,5 +7,5 @@ var ( DevLongDescription = "Starts a local development server for the current application, so it's possible to preview and test it locally before the deployment" IsFirewall = "Indicates whether the function to be run is intended for the Firewall" PortFlag = "Indicates which port to use when starting localhost environment" - SkipFrameworkBuild = "Indicates whether to bypass the framework build phase before executing 'azion build'" + SkipFrameworkBuild = "Indicates whether to bypass the framework build phase" ) diff --git a/messages/link/messages.go b/messages/link/messages.go index dd69a2f0..7a1488a6 100644 --- a/messages/link/messages.go +++ b/messages/link/messages.go @@ -35,6 +35,7 @@ var ( AskWorkflow = "Would you like to add a GitHub Actions workflow for automated deployment? (Y/n)" WrittenWorkflow = "Successfully created GitHub Actions workflow file at .github/workflows/azion-deploy.yml\n" SkipFrameworkBuild = "Indicates whether to bypass the framework build phase before executing 'azion build'" + PresetAutoDetected = "Preset '%s' automatically detected from info.json\n" ) const ( diff --git a/messages/network_list/errors.go b/messages/network_list/errors.go new file mode 100644 index 00000000..ae413705 --- /dev/null +++ b/messages/network_list/errors.go @@ -0,0 +1,12 @@ +package networklist + +import "errors" + +var ( + ErrorGetNetworkLists = errors.New("Failed to list your network lists. Check your settings and try again. If the error persists, contact Azion support.") + ErrorFailToDeleteNetworkList = errors.New("Failed to delete Network List: %w") + ErrorGetNetworkList = errors.New("Failed to describe Network List: %w") + ErrorCreateNetworkList = errors.New("Failed to create Network List: %w") + ErrorUpdateNetworkList = errors.New("Failed to update Network List: %w") + ErrorActiveFlag = errors.New("Invalid value for --active flag") +) diff --git a/messages/network_list/messages.go b/messages/network_list/messages.go new file mode 100644 index 00000000..c21bc6e1 --- /dev/null +++ b/messages/network_list/messages.go @@ -0,0 +1,44 @@ +package networklist + +var ( + Usage = "network-list" + + ListShortDescription = "Displays your Network Lists in a list" + ListLongDescription = "Displays all your Network Lists in a list" + ListHelpFlag = "Displays more information about the 'list network-list' command" + + DeleteShortDescription = "Deletes a Network List" + DeleteLongDescription = "Deletes a Network List based on a given ID" + DeleteHelpFlag = "Displays more information about the 'delete network-list' command" + DeleteOutputSuccess = "Network List %s was successfully deleted" + + DescribeShortDescription = "Displays a Network List" + DescribeLongDescription = "Displays a Network List based on a given ID" + DescribeHelpFlag = "Displays more information about the 'describe network-list' command" + + CreateShortDescription = "Creates a Network List" + CreateLongDescription = "Creates a Network List based on given attributes" + CreateHelpFlag = "Displays more information about the 'create network-list' command" + CreateOutputSuccess = "Created Network List with ID %d" + + UpdateShortDescription = "Updates a Network List" + UpdateLongDescription = "Updates a Network List based on a given ID" + UpdateHelpFlag = "Displays more information about the 'update network-list' command" + UpdateOutputSuccess = "Updated Network List with ID %d" + UpdateAskNetworkListID = "Enter the Network List's ID:" + + AskNetworkListID = "Enter the Network List's ID:" + AskName = "Enter the Network List's name:" + AskType = "Select the Network List type:" + AskItems = "Enter the items (comma-separated):" + AskActive = "Should the Network List be active?" + + FlagID = "Unique identifier of the Network List" + FlagName = "Name of the Network List" + FlagType = "Type of the Network List (asn, countries, ip_cidr)" + FlagItems = "Items for the Network List (comma-separated)" + FlagAddItem = "Add items to the Network List (comma-separated)" + FlagRemoveItem = "Remove items from the Network List (comma-separated)" + FlagActive = "Whether the Network List is active" + FlagIn = "Path to a JSON file containing the Network List attributes" +) diff --git a/messages/rollback/errors.go b/messages/rollback/errors.go index 6ec80abc..5ca1230e 100644 --- a/messages/rollback/errors.go +++ b/messages/rollback/errors.go @@ -6,4 +6,5 @@ var ( ERRORROLLBACK = errors.New("Failed to roll back to previous static files") ERRORNEEDSDEPLOY = errors.New("You cannot use the rollback command unless you have already deployed this project. Please check if you are in the correct working directory") ERRORAZION = errors.New("Failed to open the azion.json file. The file doesn't exist, is corrupted, or has an invalid JSON format") + ERRORNOPREVIOUS = errors.New("No previous deployment found to roll back to. You need at least two deployments to use the rollback command") ) diff --git a/messages/rollback/messages.go b/messages/rollback/messages.go index 0c2c71aa..f4ba932f 100644 --- a/messages/rollback/messages.go +++ b/messages/rollback/messages.go @@ -5,8 +5,8 @@ const ( SHORTDESCRIPTION = "Sets static files from a previous deploy" LONGDESCRIPTION = "Sets static files from a previous deploy within the same bucket" FLAGHELP = "Displays more information about the rollback command" - FLAGORIGINKEY = "Origin key of the origin used for storage of static files" + FLAGCONNECTORID = "Connector ID of the storage connector used for static files" CONFFLAG = "Relative path to where your custom azion.json and args.json files are stored" - ASKORIGIN = "Enter the key of the Origin you wish to update:" + ASKCONNECTOR = "Enter the ID of the Connector you wish to update:" SUCCESS = "Static files rolled back successfully" ) diff --git a/messages/sync/errors.go b/messages/sync/errors.go index 79e9af04..50c580e3 100644 --- a/messages/sync/errors.go +++ b/messages/sync/errors.go @@ -3,9 +3,14 @@ package sync import "errors" var ( - ERRORSYNC = "Failed to synchronize local resources with remote resources: %s" - ERRORNOTDEPLOYED = errors.New("Failed to synchronize local resources with remote resources: You must deploy your project at least once before trying to synchronize with remote resources") - ERRORWRITEMANIFEST = errors.New("Failed to write manifest.json file.") - ERRORMARSHALMANIFEST = errors.New("Failed to marshal manifest structure.") - INVALIDFORMAT = errors.New("Invalid format for azion.config file") + ERRORSYNC = "Failed to synchronize local resources with remote resources: %s" + ERRORNOTDEPLOYED = errors.New("Failed to synchronize local resources with remote resources: You must deploy your project at least once before trying to synchronize with remote resources") + ERRORWRITEMANIFEST = errors.New("Failed to write manifest.json file.") + ERRORMARSHALMANIFEST = errors.New("Failed to marshal manifest structure.") + INVALIDFORMAT = errors.New("Invalid format for azion.config file") + ERRORMARSHALCRITERIA = "failed to marshal criteria: %w" + ERRORUNMARSHALCRITERIA = "failed to unmarshal criteria: %w" + ERRORMARSHALBEHAVIORS = "failed to marshal behaviors: %w" + ERRORUNMARSHALBEHAVIORS = "failed to unmarshal behaviors: %w" + ERRORLISTRESPONSERULES = "failed to list response phase rules: %w" ) diff --git a/pkg/api/network_list/client.go b/pkg/api/network_list/client.go new file mode 100644 index 00000000..139ecd8c --- /dev/null +++ b/pkg/api/network_list/client.go @@ -0,0 +1,29 @@ +package networklist + +import ( + "net/http" + "time" + + "github.com/aziontech/azion-cli/pkg/cmd/version" + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" +) + +type Client struct { + apiClient *sdk.APIClient +} + +func NewClient(c *http.Client, url string, token string) *Client { + conf := sdk.NewConfiguration() + conf.HTTPClient = c + conf.AddDefaultHeader("Authorization", "token "+token) + conf.AddDefaultHeader("Accept", "application/json;version=3") + conf.UserAgent = "Azion_CLI/" + version.BinVersion + conf.Servers = sdk.ServerConfigurations{ + {URL: url}, + } + conf.HTTPClient.Timeout = 50 * time.Second + + return &Client{ + apiClient: sdk.NewAPIClient(conf), + } +} diff --git a/pkg/api/network_list/models.go b/pkg/api/network_list/models.go new file mode 100644 index 00000000..0a2644b4 --- /dev/null +++ b/pkg/api/network_list/models.go @@ -0,0 +1,21 @@ +package networklist + +import ( + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" +) + +type CreateRequest struct { + sdk.NetworkListDetailRequest +} + +func NewCreateRequest() *CreateRequest { + return &CreateRequest{} +} + +type UpdateRequest struct { + sdk.PatchedNetworkListDetailRequest +} + +func NewUpdateRequest() *UpdateRequest { + return &UpdateRequest{} +} diff --git a/pkg/api/network_list/network_list.go b/pkg/api/network_list/network_list.go new file mode 100644 index 00000000..0a39c286 --- /dev/null +++ b/pkg/api/network_list/network_list.go @@ -0,0 +1,120 @@ +package networklist + +import ( + "context" + + "github.com/aziontech/azion-cli/pkg/contracts" + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/utils" + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" + "go.uber.org/zap" +) + +func (c *Client) List(ctx context.Context, opts *contracts.ListOptions) (*sdk.PaginatedNetworkListList, error) { + logger.Debug("List network lists") + if opts.OrderBy == "" { + opts.OrderBy = "id" + } + resp, httpResp, err := c.apiClient.NetworkListsAPI.ListNetworkLists(ctx). + Ordering(opts.OrderBy). + Page(opts.Page). + PageSize(opts.PageSize). + Search(opts.Sort). + Execute() + + if err != nil { + errBody := "" + if httpResp != nil { + logger.Debug("Error while listing the network lists", zap.Error(err)) + errBody, err = utils.LogAndRewindBodyV4(httpResp) + if err != nil { + return nil, err + } + } + return nil, utils.ErrorPerStatusCodeV4(errBody, httpResp, err) + } + + return resp, nil +} + +func (c *Client) Delete(ctx context.Context, id string) error { + logger.Debug("Delete network list") + request := c.apiClient.NetworkListsAPI.DestroyNetworkList(ctx, id) + + _, httpResp, err := request.Execute() + + if err != nil { + errBody := "" + if httpResp != nil { + logger.Debug("Error while deleting a network list", zap.Error(err)) + errBody, err = utils.LogAndRewindBodyV4(httpResp) + if err != nil { + return err + } + } + return utils.ErrorPerStatusCodeV4(errBody, httpResp, err) + } + + return nil +} + +func (c *Client) Get(ctx context.Context, id string) (sdk.NetworkListDetail, error) { + logger.Debug("Get network list") + request := c.apiClient.NetworkListsAPI.RetrieveNetworkList(ctx, id) + + res, httpResp, err := request.Execute() + if err != nil { + errBody := "" + if httpResp != nil { + logger.Debug("Error while getting a network list", zap.Error(err)) + errBody, err = utils.LogAndRewindBodyV4(httpResp) + if err != nil { + return sdk.NetworkListDetail{}, err + } + } + return sdk.NetworkListDetail{}, utils.ErrorPerStatusCodeV4(errBody, httpResp, err) + } + + return res.Data, nil +} + +func (c *Client) Create(ctx context.Context, req *CreateRequest) (sdk.NetworkListDetail, error) { + logger.Debug("Create network list") + + request := c.apiClient.NetworkListsAPI.CreateNetworkList(ctx).NetworkListDetailRequest(req.NetworkListDetailRequest) + + edgeFuncResponse, httpResp, err := request.Execute() + if err != nil { + errBody := "" + if httpResp != nil { + logger.Debug("Error while creating a network list", zap.Error(err), zap.Any("Name", req.Name)) + errBody, err = utils.LogAndRewindBodyV4(httpResp) + if err != nil { + return sdk.NetworkListDetail{}, err + } + } + return sdk.NetworkListDetail{}, utils.ErrorPerStatusCodeV4(errBody, httpResp, err) + } + + return edgeFuncResponse.Data, nil +} + +func (c *Client) Update(ctx context.Context, req *UpdateRequest, id string) (sdk.NetworkListDetail, error) { + logger.Debug("Update network list", zap.Any("ID", id), zap.Any("Name", req.Name)) + request := c.apiClient.NetworkListsAPI.PartialUpdateNetworkList(ctx, id).PatchedNetworkListDetailRequest(req.PatchedNetworkListDetailRequest) + + edgeFuncResponse, httpResp, err := request.Execute() + if err != nil { + errBody := "" + if httpResp != nil { + logger.Debug("Error while updating a network list", zap.Error(err), zap.Any("ID", id), zap.Any("Name", req.Name)) + errBody, err = utils.LogAndRewindBodyV4(httpResp) + if err != nil { + return sdk.NetworkListDetail{}, err + } + } + return sdk.NetworkListDetail{}, utils.ErrorPerStatusCodeV4(errBody, httpResp, err) + } + + return edgeFuncResponse.Data, nil +} diff --git a/pkg/cmd/build/run.go b/pkg/cmd/build/run.go index 968971b2..b2233b20 100644 --- a/pkg/cmd/build/run.go +++ b/pkg/cmd/build/run.go @@ -3,6 +3,7 @@ package build import ( "fmt" "strconv" + "strings" msg "github.com/aziontech/azion-cli/messages/build" "github.com/aziontech/azion-cli/pkg/contracts" @@ -22,17 +23,20 @@ func (b *BuildCmd) run(fields *contracts.BuildInfo, msgs *[]string) error { return msg.ErrorBuilding } - var vulcanParams string + var paramsBuilder strings.Builder if fields.Preset != "" { - vulcanParams = " --preset " + fields.Preset + paramsBuilder.WriteString(" --preset ") + paramsBuilder.WriteString(fields.Preset) conf.Preset = fields.Preset } else { - vulcanParams = " --preset " + conf.Preset + paramsBuilder.WriteString(" --preset ") + paramsBuilder.WriteString(conf.Preset) } if fields.Entry != "" { - vulcanParams += " --entry " + fields.Entry + paramsBuilder.WriteString(" --entry ") + paramsBuilder.WriteString(fields.Entry) } if fields.NodePolyfills != "" { @@ -40,7 +44,8 @@ func (b *BuildCmd) run(fields *contracts.BuildInfo, msgs *[]string) error { if err != nil { return fmt.Errorf("%w: %s", msg.ErrorPolyfills, fields.NodePolyfills) } - vulcanParams += " --polyfills " + fields.NodePolyfills + paramsBuilder.WriteString(" --polyfills ") + paramsBuilder.WriteString(fields.NodePolyfills) } if fields.OwnWorker != "" { @@ -48,13 +53,14 @@ func (b *BuildCmd) run(fields *contracts.BuildInfo, msgs *[]string) error { if err != nil { return fmt.Errorf("%w: %s", msg.ErrorWorker, fields.OwnWorker) } - vulcanParams += " --worker " + fields.OwnWorker + paramsBuilder.WriteString(" --worker ") + paramsBuilder.WriteString(fields.OwnWorker) } if fields.SkipFramework { - vulcanParams += " --skip-framework-build" + paramsBuilder.WriteString(" --skip-framework-build") } vul := vulcanPkg.NewVulcan() - return b.vulcan(vul, conf, vulcanParams, fields, msgs) + return b.vulcan(vul, conf, paramsBuilder.String(), fields, msgs) } diff --git a/pkg/cmd/create/cache_setting/cache_setting.go b/pkg/cmd/create/cache_setting/cache_setting.go index 20933a15..fa7e1a9b 100644 --- a/pkg/cmd/create/cache_setting/cache_setting.go +++ b/pkg/cmd/create/cache_setting/cache_setting.go @@ -27,7 +27,6 @@ type Fields struct { Name string browserCacheBehavior string browserCacheMaxAge int64 - adaptiveDeliveryAction string cacheByQueryString string queryStringFields []string cacheByCookies string diff --git a/pkg/cmd/create/create.go b/pkg/cmd/create/create.go index f2a193b7..4bbb58f7 100644 --- a/pkg/cmd/create/create.go +++ b/pkg/cmd/create/create.go @@ -8,6 +8,7 @@ import ( edgeConnector "github.com/aziontech/azion-cli/pkg/cmd/create/connector" edgeFunction "github.com/aziontech/azion-cli/pkg/cmd/create/function" functionInstance "github.com/aziontech/azion-cli/pkg/cmd/create/function_instance" + networkList "github.com/aziontech/azion-cli/pkg/cmd/create/network_list" origin "github.com/aziontech/azion-cli/pkg/cmd/create/origin" token "github.com/aziontech/azion-cli/pkg/cmd/create/personal_token" profile "github.com/aziontech/azion-cli/pkg/cmd/create/profile" @@ -29,6 +30,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { $ azion create application -h $ azion create connector -h $ azion create workload -h + $ azion create network-list -h `), RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() @@ -48,6 +50,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(edgeConnector.NewCmd(f)) cmd.AddCommand(functionInstance.NewCmd(f)) cmd.AddCommand(profile.NewCmd(f)) + cmd.AddCommand(networkList.NewCmd(f)) cmd.Flags().BoolP("help", "h", false, msg.FlagHelp) return cmd diff --git a/pkg/cmd/create/network_list/fixtures/create.json b/pkg/cmd/create/network_list/fixtures/create.json new file mode 100644 index 00000000..10494c16 --- /dev/null +++ b/pkg/cmd/create/network_list/fixtures/create.json @@ -0,0 +1,6 @@ +{ + "name": "Created via File", + "type": "ip_cidr", + "items": ["192.168.1.0/24", "10.0.0.0/8"], + "active": true +} diff --git a/pkg/cmd/create/network_list/network_list.go b/pkg/cmd/create/network_list/network_list.go new file mode 100644 index 00000000..a5a2d6d8 --- /dev/null +++ b/pkg/cmd/create/network_list/network_list.go @@ -0,0 +1,137 @@ +package networklist + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/MakeNowJust/heredoc" + msg "github.com/aziontech/azion-cli/messages/network_list" + api "github.com/aziontech/azion-cli/pkg/api/network_list" + "github.com/aziontech/azion-cli/pkg/cmdutil" + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/pkg/output" + "github.com/aziontech/azion-cli/utils" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "go.uber.org/zap" +) + +type Fields struct { + Name string + Type string + Items string + Active string + InPath string +} + +func NewCmd(f *cmdutil.Factory) *cobra.Command { + fields := &Fields{} + + cmd := &cobra.Command{ + Use: msg.Usage, + Short: msg.CreateShortDescription, + Long: msg.CreateLongDescription, + SilenceUsage: true, + SilenceErrors: true, + Example: heredoc.Doc(` + $ azion create network-list --name "My IP List" --type ip_cidr --items "192.168.0.1/32,10.0.0.0/8" + $ azion create network-list --name "ASN List" --type asn --items "1234,5678" --active true + $ azion create network-list --file "./network_list.json" + `), + RunE: func(cmd *cobra.Command, args []string) error { + request := api.NewCreateRequest() + + if cmd.Flags().Changed("file") { + err := utils.FlagFileUnmarshalJSON(fields.InPath, &request) + if err != nil { + logger.Debug("Failed to unmarshal file", zap.Error(err)) + return utils.ErrorUnmarshalReader + } + } else { + err := createRequestFromFlags(cmd, fields, request) + if err != nil { + return err + } + } + + client := api.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + + ctx := context.Background() + response, err := client.Create(ctx, request) + if err != nil { + return fmt.Errorf(msg.ErrorCreateNetworkList.Error(), err) + } + + creatOut := output.GeneralOutput{ + Msg: fmt.Sprintf(msg.CreateOutputSuccess, response.GetId()), + Out: f.IOStreams.Out, + } + return output.Print(&creatOut) + }, + } + + flags := cmd.Flags() + addFlags(flags, fields) + + return cmd +} + +func addFlags(flags *pflag.FlagSet, fields *Fields) { + flags.StringVar(&fields.Name, "name", "", msg.FlagName) + flags.StringVar(&fields.Type, "type", "", msg.FlagType) + flags.StringVar(&fields.Items, "items", "", msg.FlagItems) + flags.StringVar(&fields.Active, "active", "", msg.FlagActive) + flags.StringVar(&fields.InPath, "file", "", msg.FlagIn) + flags.BoolP("help", "h", false, msg.CreateHelpFlag) +} + +func createRequestFromFlags(cmd *cobra.Command, fields *Fields, request *api.CreateRequest) error { + + if !cmd.Flags().Changed("name") { + answers, err := utils.AskInput(msg.AskName) + if err != nil { + logger.Debug("Error while parsing answer", zap.Error(err)) + return utils.ErrorParseResponse + } + fields.Name = answers + } + + if !cmd.Flags().Changed("type") { + answers, err := utils.Select(utils.NewSelectPrompter(msg.AskType, []string{"asn", "countries", "ip_cidr"})) + if err != nil { + logger.Debug("Error while parsing answer", zap.Error(err)) + return utils.ErrorParseResponse + } + fields.Type = answers + } + + if !cmd.Flags().Changed("items") { + answers, err := utils.AskInput(msg.AskItems) + if err != nil { + logger.Debug("Error while parsing answer", zap.Error(err)) + return utils.ErrorParseResponse + } + fields.Items = answers + } + + request.SetName(fields.Name) + request.SetType(fields.Type) + + items := strings.Split(fields.Items, ",") + for i := range items { + items[i] = strings.TrimSpace(items[i]) + } + request.SetItems(items) + + if cmd.Flags().Changed("active") { + isActive, err := strconv.ParseBool(fields.Active) + if err != nil { + return fmt.Errorf("%w: %s", msg.ErrorActiveFlag, fields.Active) + } + request.SetActive(isActive) + } + + return nil +} diff --git a/pkg/cmd/create/network_list/network_list_test.go b/pkg/cmd/create/network_list/network_list_test.go new file mode 100644 index 00000000..d7388953 --- /dev/null +++ b/pkg/cmd/create/network_list/network_list_test.go @@ -0,0 +1,213 @@ +package networklist + +import ( + "fmt" + "net/http" + "testing" + + "github.com/aziontech/azion-cli/pkg/logger" + "go.uber.org/zap/zapcore" + + msg "github.com/aziontech/azion-cli/messages/network_list" + "github.com/aziontech/azion-cli/pkg/httpmock" + "github.com/aziontech/azion-cli/pkg/testutils" + "github.com/stretchr/testify/require" +) + +var errorResponse string = ` +{ + "errors": [ + { + "status": "500", + "code": "500", + "title": "string", + "detail": "string", + "source": { + "pointer": "string", + "parameter": "string", + "header": "string" + }, + "meta": { + "property1": null, + "property2": null + } + } + ] +} +` + +var successResponse string = ` +{ + "data": { + "id": 1337, + "name": "My IP List", + "type": "ip_cidr", + "items": [ + "192.168.0.1/32", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + +func TestCreate(t *testing.T) { + logger.New(zapcore.DebugLevel) + t.Run("create new Network List", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "My IP List", "--type", "ip_cidr", "--items", "192.168.0.1/32,10.0.0.0/8", "--active", "true"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.CreateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("create ASN Network List", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "ASN List", "--type", "asn", "--items", "1234,5678"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.CreateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("create countries Network List", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "Countries List", "--type", "countries", "--items", "US,BR,JP"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.CreateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("bad request", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.StatusStringResponse(http.StatusBadRequest, "Invalid"), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "Bad List", "--type", "invalid_type", "--items", "test"}) + + err := cmd.Execute() + + require.Error(t, err) + }) + + t.Run("create with file", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--file", "./fixtures/create.json"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.CreateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("Error Field active not is boolean", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.JSONFromString(successResponse), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "Test List", "--type", "ip_cidr", "--items", "192.168.1.0/24", "--active", "invalid_bool"}) + + err := cmd.Execute() + stringErr := fmt.Sprintf("%s: %s", msg.ErrorActiveFlag, "invalid_bool") + if stringErr == err.Error() { + return + } + t.Fatalf("Error: %q", err) + }) + + t.Run("Error create network list request api", func(t *testing.T) { + t.Parallel() + + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("POST", "workspace/network_lists"), + httpmock.StatusStringResponse(http.StatusInternalServerError, errorResponse), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--name", "Error List", "--type", "ip_cidr", "--items", "192.168.1.0/24", "--active", "true"}) + + err := cmd.Execute() + if err != nil { + return + } + t.Fatalf("Error: %q", err) + }) +} diff --git a/pkg/cmd/delete/delete.go b/pkg/cmd/delete/delete.go index 12ed32b4..44ef4189 100644 --- a/pkg/cmd/delete/delete.go +++ b/pkg/cmd/delete/delete.go @@ -8,6 +8,7 @@ import ( connector "github.com/aziontech/azion-cli/pkg/cmd/delete/connector" function "github.com/aziontech/azion-cli/pkg/cmd/delete/function" functionInstance "github.com/aziontech/azion-cli/pkg/cmd/delete/function_instance" + networkList "github.com/aziontech/azion-cli/pkg/cmd/delete/network_list" origin "github.com/aziontech/azion-cli/pkg/cmd/delete/origin" token "github.com/aziontech/azion-cli/pkg/cmd/delete/personal_token" profile "github.com/aziontech/azion-cli/pkg/cmd/delete/profile" @@ -28,6 +29,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { $ azion delete application -h $ azion delete workload -h $ azion delete origin -h + $ azion delete network-list -h `), RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() @@ -46,6 +48,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(connector.NewCmd(f)) cmd.AddCommand(functionInstance.NewCmd(f)) cmd.AddCommand(profile.NewCmd(f)) + cmd.AddCommand(networkList.NewCmd(f)) cmd.Flags().BoolP("help", "h", false, msg.FlagHelp) return cmd diff --git a/pkg/cmd/delete/network_list/network_list.go b/pkg/cmd/delete/network_list/network_list.go new file mode 100644 index 00000000..36f5b418 --- /dev/null +++ b/pkg/cmd/delete/network_list/network_list.go @@ -0,0 +1,86 @@ +package networklist + +import ( + "context" + "fmt" + + "github.com/MakeNowJust/heredoc" + msg "github.com/aziontech/azion-cli/messages/network_list" + api "github.com/aziontech/azion-cli/pkg/api/network_list" + "github.com/aziontech/azion-cli/pkg/cmdutil" + "github.com/aziontech/azion-cli/pkg/iostreams" + "github.com/aziontech/azion-cli/pkg/output" + "github.com/aziontech/azion-cli/utils" + "github.com/spf13/cobra" +) + +var networkListID string + +type DeleteCmd struct { + Io *iostreams.IOStreams + ReadInput func(string) (string, error) + DeleteNetworkList func(context.Context, string) error + AskInput func(string) (string, error) +} + +func NewDeleteCmd(f *cmdutil.Factory) *DeleteCmd { + return &DeleteCmd{ + Io: f.IOStreams, + ReadInput: func(prompt string) (string, error) { + return utils.AskInput(prompt) + }, + DeleteNetworkList: func(ctx context.Context, networkListID string) error { + client := api.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + return client.Delete(ctx, networkListID) + }, + AskInput: utils.AskInput, + } +} + +func NewCobraCmd(delete *DeleteCmd, f *cmdutil.Factory) *cobra.Command { + cobraCmd := &cobra.Command{ + Use: msg.Usage, + Short: msg.DeleteShortDescription, + Long: msg.DeleteLongDescription, + SilenceUsage: true, + SilenceErrors: true, + Example: heredoc.Doc(` + $ azion delete network-list --network-list-id 1234 + `), + RunE: func(cmd *cobra.Command, args []string) error { + var err error + + if !cmd.Flags().Changed("network-list-id") { + answer, err := delete.AskInput(msg.AskNetworkListID) + if err != nil { + return err + } + + networkListID = answer + } + + ctx := context.Background() + + err = delete.DeleteNetworkList(ctx, networkListID) + if err != nil { + return fmt.Errorf(msg.ErrorFailToDeleteNetworkList.Error(), err) + } + + deleteOut := output.GeneralOutput{ + Msg: fmt.Sprintf(msg.DeleteOutputSuccess, networkListID), + Out: f.IOStreams.Out, + Flags: f.Flags, + } + return output.Print(&deleteOut) + }, + } + + cobraCmd.Flags().StringVar(&networkListID, "network-list-id", "", msg.FlagID) + cobraCmd.Flags().BoolP("help", "h", false, msg.DeleteHelpFlag) + + return cobraCmd +} + +func NewCmd(f *cmdutil.Factory) *cobra.Command { + return NewCobraCmd(NewDeleteCmd(f), f) +} diff --git a/pkg/cmd/delete/network_list/network_list_test.go b/pkg/cmd/delete/network_list/network_list_test.go new file mode 100644 index 00000000..bc471a8b --- /dev/null +++ b/pkg/cmd/delete/network_list/network_list_test.go @@ -0,0 +1,134 @@ +package networklist + +import ( + "fmt" + "testing" + + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/utils" + "go.uber.org/zap/zapcore" + + msg "github.com/aziontech/azion-cli/messages/network_list" + "github.com/aziontech/azion-cli/pkg/httpmock" + "github.com/aziontech/azion-cli/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func mockNetworkListID(msg string) (string, error) { + return "1234", nil +} + +func mockInvalidNetworkListID(msg string) (string, error) { + return "invalid", nil +} + +func mockParseErrorNetworkListID(msg string) (string, error) { + return "invalid", utils.ErrorParseResponse +} + +func TestDeleteWithAskInput(t *testing.T) { + logger.New(zapcore.DebugLevel) + + tests := []struct { + name string + networkListID string + method string + endpoint string + statusCode int + responseBody string + expectedOutput string + expectError bool + mockInputs func(string) (string, error) + mockError error + }{ + { + name: "delete network list by id", + networkListID: "1234", + method: "DELETE", + endpoint: "workspace/network_lists/1234", + statusCode: 204, + responseBody: "", + expectedOutput: fmt.Sprintf(msg.DeleteOutputSuccess, "1234"), + expectError: false, + mockInputs: mockNetworkListID, + mockError: nil, + }, + { + name: "delete network list - not found", + networkListID: "1234", + method: "DELETE", + endpoint: "workspace/network_lists/1234", + statusCode: 404, + responseBody: "Not Found", + expectedOutput: "", + expectError: true, + mockInputs: mockNetworkListID, + mockError: fmt.Errorf("Failed to parse your response. Check your response and try again. If the error persists, contact Azion support"), + }, + { + name: "error in input", + networkListID: "1234", + method: "DELETE", + endpoint: "workspace/network_lists/invalid", + statusCode: 400, + responseBody: "Bad Request", + expectedOutput: "", + expectError: true, + mockInputs: mockInvalidNetworkListID, + mockError: fmt.Errorf("invalid argument \"\" for \"--network-list-id\" flag: strconv.ParseInt: parsing \"\": invalid syntax"), + }, + { + name: "ask for network list id success", + networkListID: "", + method: "DELETE", + endpoint: "workspace/network_lists/1234", + statusCode: 204, + responseBody: "", + expectedOutput: fmt.Sprintf(msg.DeleteOutputSuccess, "1234"), + expectError: false, + mockInputs: mockNetworkListID, + mockError: nil, + }, + { + name: "error - parse answer", + networkListID: "", + method: "", + endpoint: "", + statusCode: 0, + responseBody: "", + expectedOutput: "", + expectError: true, + mockInputs: mockParseErrorNetworkListID, + mockError: utils.ErrorParseResponse, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mock := &httpmock.Registry{} + mock.Register( + httpmock.REST(tt.method, tt.endpoint), + httpmock.StatusStringResponse(tt.statusCode, tt.responseBody), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + deleteCmd := NewDeleteCmd(f) + deleteCmd.AskInput = tt.mockInputs + cobraCmd := NewCobraCmd(deleteCmd, f) + + if tt.networkListID != "" { + cobraCmd.SetArgs([]string{"--network-list-id", tt.networkListID}) + } + + _, err := cobraCmd.ExecuteC() + if tt.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expectedOutput, stdout.String()) + } + }) + } +} diff --git a/pkg/cmd/delete/storage/bucket/bucket.go b/pkg/cmd/delete/storage/bucket/bucket.go index 94e1a985..0e06b3b5 100644 --- a/pkg/cmd/delete/storage/bucket/bucket.go +++ b/pkg/cmd/delete/storage/bucket/bucket.go @@ -16,6 +16,7 @@ import ( "github.com/aziontech/azion-cli/pkg/schedule" "github.com/aziontech/azion-cli/utils" "github.com/spf13/cobra" + "go.uber.org/zap" ) type DeleteBucketCmd struct { @@ -92,7 +93,7 @@ func NewBucketCmd(delete *DeleteBucketCmd, f *cmdutil.Factory) *cobra.Command { if err != nil { if strings.Contains(err.Error(), msg.ERROR_NO_EMPTY_BUCKET) { logger.FInfo(f.IOStreams.Out, "Bucket deletion was scheduled successfully\n") - return schedule.NewSchedule(nil, bucketName, schedule.DELETE_BUCKET) + return schedule.NewSchedule(nil, f, bucketName, schedule.DELETE_BUCKET) } else { return fmt.Errorf(msg.ERROR_DELETE_BUCKET, err.Error()) } @@ -123,15 +124,23 @@ func NewBucket(f *cmdutil.Factory) *cobra.Command { } func deleteAllObjects(client *api.Client, ctx context.Context, name, continuationToken string) error { - objects, err := client.ListObject(ctx, name, &contracts.ListOptions{ContinuationToken: continuationToken}) - if err != nil { - return err - } - for _, obj := range objects.Results { - err := client.DeleteObject(ctx, name, obj.GetKey()) + for { + objects, err := client.ListObject(ctx, name, &contracts.ListOptions{ContinuationToken: continuationToken}) if err != nil { return err } + for _, obj := range objects.Results { + logger.Debug("Deleting object", zap.Any("object-key", obj.GetKey())) + err := client.DeleteObject(ctx, name, obj.GetKey()) + if err != nil { + return err + } + } + logger.Debug("continuing to next page", zap.Any("continuation-token", objects.GetContinuationToken())) + if contToken, ok := objects.GetContinuationTokenOk(); contToken == nil || !ok { + break + } + continuationToken = objects.GetContinuationToken() } return nil } diff --git a/pkg/cmd/deploy/deploy.go b/pkg/cmd/deploy/deploy.go index ebcea03e..185ff67a 100644 --- a/pkg/cmd/deploy/deploy.go +++ b/pkg/cmd/deploy/deploy.go @@ -286,7 +286,7 @@ func captureLogs(execId, token string, cmd *DeployCmd) error { req.Header.Set("content-type", "application/json; version=3") req.Header.Set("Authorization", "Token "+token) - // Send the request + // Reuse single HTTP client for all requests client := &http.Client{} logTime := time.Now() lastLog := "" @@ -299,10 +299,10 @@ func captureLogs(execId, token string, cmd *DeployCmd) error { logger.Debug("Error sending request", zap.Error(err)) return err } - defer resp.Body.Close() // Read the response body, err := io.ReadAll(resp.Body) + resp.Body.Close() if err != nil { return err } @@ -332,7 +332,7 @@ func captureLogs(execId, token string, cmd *DeployCmd) error { time.Sleep(7 * time.Second) continue case "succeeded": - // Create a new HTTP request + // Create a new HTTP request for results requestResults, err := http.NewRequest("GET", resultsURL, bytes.NewBuffer([]byte{})) if err != nil { logger.Debug("Error creating request", zap.Error(err)) @@ -344,18 +344,16 @@ func captureLogs(execId, token string, cmd *DeployCmd) error { requestResults.Header.Set("content-type", "application/json; version=3") requestResults.Header.Set("Authorization", "Token "+token) - // Send the request - clientResults := &http.Client{} - - respResults, err := clientResults.Do(requestResults) + // Reuse the same client + respResults, err := client.Do(requestResults) if err != nil { logger.Debug("Error sending request", zap.Error(err)) return err } - defer respResults.Body.Close() // Read the response body, err := io.ReadAll(respResults.Body) + respResults.Body.Close() if err != nil { return err } diff --git a/pkg/cmd/deploy/upload.go b/pkg/cmd/deploy/upload.go index f845b32d..4fd40794 100644 --- a/pkg/cmd/deploy/upload.go +++ b/pkg/cmd/deploy/upload.go @@ -22,7 +22,6 @@ import ( ) var ( - Jobs chan contracts.FileOps Retries int64 ) @@ -95,7 +94,7 @@ func uploadFiles(f *cmdutil.Factory, conf *contracts.AzionApplicationOptions, ms return err } - err = CreateZipsInBatches(allFiles) + createdZipPaths, err := CreateZipsInBatches(allFiles) if err != nil { return err } @@ -106,9 +105,22 @@ func uploadFiles(f *cmdutil.Factory, conf *contracts.AzionApplicationOptions, ms } }() - listZip, err := ReadZip() - if err != nil { - return err + // Open the created zip files + listZip := make([]contracts.FileOps, 0, len(createdZipPaths)) + tempDir := os.TempDir() + for _, zipPath := range createdZipPaths { + f, err := os.Open(zipPath) + if err != nil { + logger.Debug("Error opening ZIP "+zipPath, zap.Error(err)) + continue + } + + fileString := strings.TrimPrefix(zipPath, tempDir) + fileOptions := contracts.FileOps{ + Path: fileString, + FileContent: f, + } + listZip = append(listZip, fileOptions) } numFiles := len(listZip) diff --git a/pkg/cmd/deploy/zip.go b/pkg/cmd/deploy/zip.go index f21bdd0d..3117aecc 100644 --- a/pkg/cmd/deploy/zip.go +++ b/pkg/cmd/deploy/zip.go @@ -14,10 +14,11 @@ import ( "go.uber.org/zap" ) -func CreateZipsInBatches(files []contracts.FileOps) error { +func CreateZipsInBatches(files []contracts.FileOps) ([]string, error) { const maxBatchSize = 1 * 1024 * 1024 // 1MB em bytes var currentBatch []contracts.FileOps var currentSize int64 = 0 + var createdZips []string tempDir := os.TempDir() batchNumber := 1 @@ -25,16 +26,18 @@ func CreateZipsInBatches(files []contracts.FileOps) error { for _, fileOp := range files { info, err := fileOp.FileContent.Stat() if err != nil { - return fmt.Errorf("error getting info from file %s: %v", fileOp.Path, err) + return nil, fmt.Errorf("error getting info from file %s: %v", fileOp.Path, err) } fileSize := info.Size() // Check if adding this file exceeds the maximum batch size if currentSize+fileSize > maxBatchSize && len(currentBatch) > 0 { // Create ZIP for the current batch - if err := createZip(currentBatch, tempDir, batchNumber); err != nil { - return err + zipPath, err := createZip(currentBatch, tempDir, batchNumber) + if err != nil { + return nil, err } + createdZips = append(createdZips, zipPath) batchNumber++ @@ -50,22 +53,24 @@ func CreateZipsInBatches(files []contracts.FileOps) error { // Create ZIP for any remaining files if len(currentBatch) > 0 { - if err := createZip(currentBatch, tempDir, batchNumber); err != nil { - return err + zipPath, err := createZip(currentBatch, tempDir, batchNumber) + if err != nil { + return nil, err } + createdZips = append(createdZips, zipPath) } - return nil + return createdZips, nil } -func createZip(batch []contracts.FileOps, destDir string, batchNumber int) error { +func createZip(batch []contracts.FileOps, destDir string, batchNumber int) (string, error) { zipFileName := fmt.Sprintf("batch_%d.zip", batchNumber) zipFilePath := filepath.Join(destDir, zipFileName) logger.Debug("Creating ZIP file", zap.String("path", zipFilePath), zap.Int("batch", len(batch))) zipFile, err := os.Create(zipFilePath) if err != nil { - return fmt.Errorf(msg.ErrorCreateZip, zipFilePath, err) + return "", fmt.Errorf(msg.ErrorCreateZip, zipFilePath, err) } defer zipFile.Close() @@ -75,7 +80,7 @@ func createZip(batch []contracts.FileOps, destDir string, batchNumber int) error for _, fileOp := range batch { relPath, err := filepath.Rel(destDir, fileOp.Path) if err != nil { - return fmt.Errorf(msg.ErrorRelPath, fileOp.Path, err) + return "", fmt.Errorf(msg.ErrorRelPath, fileOp.Path, err) } // Replace directory separators for ZIP compatibility @@ -84,21 +89,25 @@ func createZip(batch []contracts.FileOps, destDir string, batchNumber int) error // Add a file to the ZIP with the relative path writer, err := zipWriter.Create(zipPath) if err != nil { - return fmt.Errorf(msg.ErrorCreateZip, zipPath, err) + return "", fmt.Errorf(msg.ErrorCreateZip, zipPath, err) } if _, err := fileOp.FileContent.Seek(0, io.SeekStart); err != nil { - return fmt.Errorf(msg.ErrorResetPointFile, fileOp.Path, err) + return "", fmt.Errorf(msg.ErrorResetPointFile, fileOp.Path, err) } _, err = io.Copy(writer, fileOp.FileContent) if err != nil { - return fmt.Errorf(msg.ErrorCopyContentFile, fileOp.Path, err) + return "", fmt.Errorf(msg.ErrorCopyContentFile, fileOp.Path, err) } } logger.Debug("Create ZIP file successfully", zap.String("path", zipFilePath), zap.Int("batch", len(batch))) - return nil + return zipFilePath, nil +} + +func isBatchZipFile(name string) bool { + return strings.ToLower(filepath.Ext(name)) == ".zip" && strings.HasPrefix(name, "batch") } func ReadZip() ([]contracts.FileOps, error) { @@ -112,8 +121,7 @@ func ReadZip() ([]contracts.FileOps, error) { } for _, f := range files { - if strings.ToLower(filepath.Ext(f.Name())) == ".zip" && - strings.HasPrefix(f.Name(), "batch") { + if isBatchZipFile(f.Name()) { pathZIP := filepath.Join(tempDir, f.Name()) logger.Debug("Processing ZIP file " + pathZIP) @@ -149,8 +157,7 @@ func CleanupZipFiles() error { var cleanupErrors []error for _, f := range files { - if strings.ToLower(filepath.Ext(f.Name())) == ".zip" && - strings.HasPrefix(f.Name(), "batch") { + if isBatchZipFile(f.Name()) { zipPath := filepath.Join(tempDir, f.Name()) logger.Debug("Removing temporary ZIP file", zap.String("path", zipPath)) diff --git a/pkg/cmd/deploy_remote/deploy.go b/pkg/cmd/deploy_remote/deploy.go index 7e862cb8..726d166b 100644 --- a/pkg/cmd/deploy_remote/deploy.go +++ b/pkg/cmd/deploy_remote/deploy.go @@ -94,7 +94,6 @@ func NewCobraCmd(deploy *DeployCmd) *cobra.Command { deployCmd.Flags().StringVar(&Path, "path", "", msg.EdgeApplicationDeployPathFlag) deployCmd.Flags().BoolVar(&Auto, "auto", false, msg.DeployFlagAuto) deployCmd.Flags().BoolVar(&NoPrompt, "no-prompt", false, msg.DeployFlagNoPrompt) - deployCmd.Flags().BoolVar(&SkipBuild, "skip-build", false, msg.DeployFlagSkipBuild) deployCmd.Flags().StringVar(&ProjectConf, "config-dir", "azion", msg.EdgeApplicationDeployProjectConfFlag) deployCmd.Flags().BoolVar(&Sync, "sync", false, msg.EdgeApplicationDeploySync) deployCmd.Flags().StringVar(&Env, "env", ".edge/.env", msg.EnvFlag) @@ -145,9 +144,9 @@ func (cmd *DeployCmd) Run(f *cmdutil.Factory) error { }() versionID := cmd.VersionID() - var oldprefix string + var oldprefix, newprefix string - oldprefix, conf.Prefix = conf.Prefix, versionID + oldprefix, newprefix = conf.Prefix, versionID err = checkArgsJson(cmd, ProjectConf) if err != nil { @@ -158,15 +157,19 @@ func (cmd *DeployCmd) Run(f *cmdutil.Factory) error { interpreter := cmd.Interpreter() if !SkipBuild && conf.NotFirstRun { - cmdStr := fmt.Sprintf("config replace -k '%s' -v '%s'", oldprefix, conf.Prefix) - vul := vulcanPkg.NewVulcan() - command := vul.Command("", cmdStr, cmd.F) - logger.Debug("Running the following command", zap.Any("Command", command)) - - err := cmd.commandRunInteractive(cmd.F, command) - if err != nil { - return err + if !SkipFramework { + conf.Prefix = newprefix + cmdStr := fmt.Sprintf("config replace -k '%s' -v '%s'", oldprefix, conf.Prefix) + vul := vulcanPkg.NewVulcan() + command := vul.Command("", cmdStr, cmd.F) + logger.Debug("Running the following command", zap.Any("Command", command)) + + err := cmd.commandRunInteractive(cmd.F, command) + if err != nil { + return err + } } + buildCmd := cmd.BuildCmd(f) err = buildCmd.ExternalRun(&contracts.BuildInfo{Preset: conf.Preset}, ProjectConf, &msgs, SkipFramework) if err != nil { @@ -200,7 +203,8 @@ func (cmd *DeployCmd) Run(f *cmdutil.Factory) error { } } - if !conf.NotFirstRun { + if !conf.NotFirstRun && (!SkipBuild || !SkipFramework) { + conf.Prefix = newprefix err = cmd.callBundlerInit(conf) if err != nil { return err @@ -221,6 +225,8 @@ func (cmd *DeployCmd) Run(f *cmdutil.Factory) error { // Check if directory exists; if not, we skip uploading static files if _, err := os.Stat(PathStatic); os.IsNotExist(err) { logger.Debug(msg.SkipUpload) + } else if SkipBuild || SkipFramework { + logger.Debug(msg.SkipUploadBuild) } else { for _, storage := range manifestStructure.Storage { err = cmd.uploadFiles(f, conf, &msgs, storage.Dir) diff --git a/pkg/cmd/deploy_remote/requests.go b/pkg/cmd/deploy_remote/requests.go index 285800f2..08e57203 100644 --- a/pkg/cmd/deploy_remote/requests.go +++ b/pkg/cmd/deploy_remote/requests.go @@ -7,7 +7,6 @@ import ( "strconv" "strings" - sdkv3 "github.com/aziontech/azionapi-go-sdk/edgeapplications" edgesdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" thoth "github.com/aziontech/go-thoth" @@ -15,7 +14,6 @@ import ( msg "github.com/aziontech/azion-cli/messages/deploy" apiapp "github.com/aziontech/azion-cli/pkg/api/applications" - api "github.com/aziontech/azion-cli/pkg/api/function" apiworkload "github.com/aziontech/azion-cli/pkg/api/workloads" "github.com/aziontech/azion-cli/pkg/contracts" "github.com/aziontech/azion-cli/pkg/logger" @@ -39,19 +37,22 @@ func (cmd *DeployCmd) callBundlerInit(conf *contracts.AzionApplicationOptions) e return err } - // cmdVulcanInit := "config update" - commands := []string{ - fmt.Sprintf("config replace -k '$FUNCTION_NAME' -v '%s'", conf.Name), - fmt.Sprintf("config replace -k '$APPLICATION_NAME' -v '%s'", conf.Name), - fmt.Sprintf("config replace -k '$BUCKET_NAME' -v '%s'", conf.Bucket), - fmt.Sprintf("config replace -k '$BUCKET_PREFIX' -v '%s'", conf.Prefix), - fmt.Sprintf("config replace -k '$CONNECTOR_NAME' -v '%s'", conf.Name), - fmt.Sprintf("config replace -k '$WORKLOAD_NAME' -v '%s'", conf.Name), - fmt.Sprintf("config replace -k '$DEPLOYMENT_NAME' -v '%s'", conf.Name), - fmt.Sprintf("config replace -k '$FUNCTION_INSTANCE_NAME' -v '%s'", conf.Name), + configReplacements := []struct { + key string + value string + }{ + {"$FUNCTION_NAME", conf.Name}, + {"$APPLICATION_NAME", conf.Name}, + {"$BUCKET_NAME", conf.Bucket}, + {"$BUCKET_PREFIX", conf.Prefix}, + {"$CONNECTOR_NAME", conf.Name}, + {"$WORKLOAD_NAME", conf.Name}, + {"$DEPLOYMENT_NAME", conf.Name}, + {"$FUNCTION_INSTANCE_NAME", conf.Name}, } - for _, cmdStr := range commands { + for _, replacement := range configReplacements { + cmdStr := fmt.Sprintf("config replace -k '%s' -v '%s'", replacement.key, replacement.value) command := vul.Command("", cmdStr, cmd.F) logger.Debug("Running the following command", zap.Any("Command", command)) @@ -190,13 +191,14 @@ func (cmd *DeployCmd) doRulesDeploy(ctx context.Context, conf *contracts.AzionAp authorize = utils.Confirm(cmd.F.GlobalFlagAll, msg.AskCreateCacheSettings, false) } + appIDStr := strconv.FormatInt(conf.Application.ID, 10) + if authorize { var reqCache apiapp.CreateCacheSettingsRequest reqCache.SetName(conf.Name) // create Cache Settings - strApp := strconv.FormatInt(conf.Application.ID, 10) - cache, err := client.CreateCacheSettingsNextApplication(ctx, &reqCache, strApp) + cache, err := client.CreateCacheSettingsNextApplication(ctx, &reqCache, appIDStr) if err != nil { logger.Debug("Error while creating Cache Settings", zap.Error(err)) return err @@ -206,10 +208,8 @@ func (cmd *DeployCmd) doRulesDeploy(ctx context.Context, conf *contracts.AzionAp cacheId = cache.GetId() } - appId := fmt.Sprintf("%d", conf.Application.ID) - // creates gzip and cache rules - err := client.CreateRulesEngineNextApplication(ctx, appId, cacheId, conf.Preset, authorize) + err := client.CreateRulesEngineNextApplication(ctx, appIDStr, cacheId, conf.Preset, authorize) if err != nil { logger.Debug("Error while creating rules engine", zap.Error(err)) return err @@ -218,92 +218,6 @@ func (cmd *DeployCmd) doRulesDeploy(ctx context.Context, conf *contracts.AzionAp return nil } -func (cmd *DeployCmd) createFunction(client *api.Client, ctx context.Context, conf *contracts.AzionApplicationOptions, funcToCreate contracts.AzionJsonDataFunction, msgs *[]string) (int64, error) { - reqCre := api.CreateRequest{} - - code, err := cmd.FileReader(funcToCreate.File) - if err != nil { - logger.Debug("Error while reading Function file <"+funcToCreate.File+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorCodeFlag, err) - } - - reqCre.SetCode(string(code)) - - reqCre.SetActive(true) - if funcToCreate.Name == "__DEFAULT__" || funcToCreate.Name == "" { - reqCre.SetName(conf.Name) - } else { - reqCre.SetName(funcToCreate.Name) - } - - //Read args - marshalledArgs, err := cmd.FileReader(funcToCreate.Args) - if err != nil { - logger.Debug("Error while reding args.json file <"+funcToCreate.Args+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorArgsFlag, err) - } - args := make(map[string]interface{}) - if err := cmd.Unmarshal(marshalledArgs, &args); err != nil { - logger.Debug("Error while unmarshling args.json file <"+funcToCreate.Args+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorParseArgs, err) - } - - reqCre.SetDefaultArgs(args) - response, err := client.Create(ctx, &reqCre) - if err != nil { - logger.Debug("Error while creating Function", zap.Error(err), zap.Any("Name", reqCre.Name)) - return 0, err - } - msgf := fmt.Sprintf(msg.DeployOutputEdgeFunctionCreate, response.GetName(), response.GetId()) - logger.FInfoFlags(cmd.F.IOStreams.Out, msgf, cmd.F.Format, cmd.F.Out) - *msgs = append(*msgs, msgf) - return response.GetId(), nil -} - -func (cmd *DeployCmd) updateFunction(client *api.Client, ctx context.Context, conf *contracts.AzionApplicationOptions, funcToUpdate contracts.AzionJsonDataFunction, msgs *[]string) (int64, error) { - reqUpd := api.UpdateRequest{} - - code, err := cmd.FileReader(funcToUpdate.File) - if err != nil { - logger.Debug("Error while reading Function file <"+funcToUpdate.File+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorCodeFlag, err) - } - - reqUpd.SetCode(string(code)) - - reqUpd.SetActive(true) - if funcToUpdate.Name == "__DEFAULT__" || funcToUpdate.Name == "" { - reqUpd.SetName(conf.Name) - } else { - reqUpd.SetName(funcToUpdate.Name) - } - - //Read args - marshalledArgs, err := cmd.FileReader(funcToUpdate.Args) - if err != nil { - logger.Debug("Error while reading args.json file <"+funcToUpdate.Args+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorArgsFlag, err) - } - args := make(map[string]interface{}) - if err := cmd.Unmarshal(marshalledArgs, &args); err != nil { - logger.Debug("Error while unmarshling args.json file <"+funcToUpdate.Args+">", zap.Error(err)) - return 0, fmt.Errorf("%s: %w", msg.ErrorParseArgs, err) - } - - reqUpd.SetDefaultArgs(args) - funcId := strconv.FormatInt(funcToUpdate.ID, 10) - response, err := client.Update(ctx, &reqUpd, funcId) - if err != nil { - logger.Debug("Error while updating Function", zap.Error(err), zap.Any("Name", reqUpd.Name)) - return 0, fmt.Errorf(msg.ErrorUpdateFunction.Error(), err) - } - - msgf := fmt.Sprintf(msg.DeployOutputEdgeFunctionUpdate, response.GetName(), funcToUpdate.ID) - logger.FInfoFlags(cmd.F.IOStreams.Out, msgf, cmd.F.Format, cmd.F.Out) - *msgs = append(*msgs, msgf) - return response.GetId(), nil -} - func (cmd *DeployCmd) createApplication(client *apiapp.Client, ctx context.Context, conf *contracts.AzionApplicationOptions, msgs *[]string) (int64, error) { reqApp := apiapp.CreateRequest{} if conf.Application.Name == "__DEFAULT__" || conf.Application.Name == "" { @@ -395,87 +309,6 @@ func (cmd *DeployCmd) updateWorkload(client *apiworkload.Client, ctx context.Con return workload, nil } -func prepareAddresses(addrs []string) (addresses []sdkv3.CreateOriginsRequestAddresses) { - var addr sdkv3.CreateOriginsRequestAddresses - for _, v := range addrs { - addr.Address = v - addresses = append(addresses, addr) - } - return -} - -func (cmd *DeployCmd) createInstance(ctx context.Context, client *apiapp.Client, conf *contracts.AzionApplicationOptions, funcToCreate contracts.AzionJsonDataFunction) (edgesdk.ApplicationFunctionInstance, error) { - logger.Debug("Create Instance") - - // create instance function - reqIns := apiapp.CreateInstanceRequest{} - reqIns.SetFunction(funcToCreate.ID) - - if funcToCreate.InstanceName == "__DEFAULT__" || funcToCreate.InstanceName == "" { - reqIns.SetName(conf.Name) - } else { - reqIns.SetName(funcToCreate.InstanceName) - } - reqIns.ApplicationId = conf.Application.ID - - //Read args - marshalledArgs, err := cmd.FileReader(funcToCreate.Args) - if err != nil { - logger.Debug("Error while reding args.json file <"+funcToCreate.Args+">", zap.Error(err)) - return edgesdk.ApplicationFunctionInstance{}, fmt.Errorf("%s: %w", msg.ErrorArgsFlag, err) - } - args := make(map[string]interface{}) - if err := cmd.Unmarshal(marshalledArgs, &args); err != nil { - logger.Debug("Error while unmarshling args.json file <"+funcToCreate.Args+">", zap.Error(err)) - return edgesdk.ApplicationFunctionInstance{}, fmt.Errorf("%s: %w", msg.ErrorParseArgs, err) - } - reqIns.SetArgs(args) - - appId := fmt.Sprintf("%d", conf.Application.ID) - resp, err := client.CreateFuncInstances(ctx, &reqIns, appId) - if err != nil { - return edgesdk.ApplicationFunctionInstance{}, err - } - - return resp, nil -} - -func (cmd *DeployCmd) updateInstance(ctx context.Context, client *apiapp.Client, conf *contracts.AzionApplicationOptions, funcToUpdate contracts.AzionJsonDataFunction) (edgesdk.ApplicationFunctionInstance, error) { - logger.Debug("Update Instance") - - // create instance function - reqIns := apiapp.UpdateInstanceRequest{} - reqIns.SetFunction(funcToUpdate.ID) - - if funcToUpdate.InstanceName == "__DEFAULT__" || funcToUpdate.InstanceName == "" { - reqIns.SetName(conf.Name) - } else { - reqIns.SetName(funcToUpdate.Name) - } - - //Read args - marshalledArgs, err := cmd.FileReader(funcToUpdate.Args) - if err != nil { - logger.Debug("Error while reding args.json file <"+funcToUpdate.Args+">", zap.Error(err)) - return edgesdk.ApplicationFunctionInstance{}, fmt.Errorf("%s: %w", msg.ErrorArgsFlag, err) - } - args := make(map[string]interface{}) - if err := cmd.Unmarshal(marshalledArgs, &args); err != nil { - logger.Debug("Error while unmarshling args.json file <"+funcToUpdate.Args+">", zap.Error(err)) - return edgesdk.ApplicationFunctionInstance{}, fmt.Errorf("%s: %w", msg.ErrorParseArgs, err) - } - reqIns.SetArgs(args) - - instID := strconv.FormatInt(funcToUpdate.InstanceID, 10) - appID := strconv.FormatInt(conf.Application.ID, 10) - resp, err := client.UpdateInstance(ctx, &reqIns, appID, instID) - if err != nil { - return edgesdk.ApplicationFunctionInstance{}, err - } - - return resp, nil -} - func checkArgsJson(cmd *DeployCmd, projectPath string) error { workingDir, err := cmd.GetWorkDir() if err != nil { diff --git a/pkg/cmd/deploy_remote/upload.go b/pkg/cmd/deploy_remote/upload.go index 50d69fe4..29e1083e 100644 --- a/pkg/cmd/deploy_remote/upload.go +++ b/pkg/cmd/deploy_remote/upload.go @@ -17,29 +17,12 @@ import ( var ( PathStatic = ".edge/storage" - Jobs chan contracts.FileOps Retries int64 ) func (cmd *DeployCmd) uploadFiles( f *cmdutil.Factory, conf *contracts.AzionApplicationOptions, msgs *[]string, dir string) error { - // Get total amount of files to display progress - totalFiles := 0 logger.Debug("Path to be uploaded: " + dir) - if err := cmd.FilepathWalk(dir, func(pathDir string, info os.FileInfo, err error) error { - if err != nil { - logger.Debug("Error while reading files to be uploaded", zap.Error(err)) - logger.Debug("File that caused the error: " + pathDir) - return err - } - if !info.IsDir() { - totalFiles++ - } - return nil - }); err != nil { - logger.Debug("Error while reading files to be uploaded", zap.Error(err)) - return err - } clientUpload := storage.NewClient(cmd.F.HttpClient, cmd.F.Config.GetString("storage_url"), cmd.F.Config.GetString("token")) @@ -48,26 +31,9 @@ func (cmd *DeployCmd) uploadFiles( noOfWorkers := 5 var currentFile int64 - Jobs := make(chan contracts.FileOps, totalFiles) - results := make(chan error, noOfWorkers) - - // Create worker goroutines - for i := 1; i <= noOfWorkers; i++ { - go Worker(Jobs, results, ¤tFile, clientUpload, conf, conf.Bucket) - } - - bar := progressbar.NewOptions( - totalFiles, - progressbar.OptionSetDescription("Uploading files"), - progressbar.OptionShowCount(), - progressbar.OptionSetWriter(cmd.F.IOStreams.Out), - progressbar.OptionClearOnFinish(), - ) - - if f.Silent { - bar = nil - } + // Collect all files in a single walk to avoid double traversal + var fileOps []contracts.FileOps if err := cmd.FilepathWalk(dir, func(pathDir string, info os.FileInfo, err error) error { if err != nil { return err @@ -97,14 +63,47 @@ func (cmd *DeployCmd) uploadFiles( FileContent: fileContent, } - Jobs <- fileOptions + fileOps = append(fileOps, fileOptions) } return nil }); err != nil { logger.Debug("Error while reading files to be uploaded", zap.Error(err)) return err } - close(Jobs) + + totalFiles := len(fileOps) + if totalFiles == 0 { + logger.FInfoFlags(cmd.F.IOStreams.Out, msg.UploadSuccessful, f.Format, f.Out) + *msgs = append(*msgs, msg.UploadSuccessful) + return nil + } + + // Create channels and workers after we know the file count + jobsChan := make(chan contracts.FileOps, totalFiles) + results := make(chan error, noOfWorkers) + + // Create worker goroutines + for i := 1; i <= noOfWorkers; i++ { + go Worker(jobsChan, results, ¤tFile, clientUpload, conf, conf.Bucket) + } + + bar := progressbar.NewOptions( + totalFiles, + progressbar.OptionSetDescription("Uploading files"), + progressbar.OptionShowCount(), + progressbar.OptionSetWriter(cmd.F.IOStreams.Out), + progressbar.OptionClearOnFinish(), + ) + + if f.Silent { + bar = nil + } + + // Queue all files for upload + for _, fileOp := range fileOps { + jobsChan <- fileOp + } + close(jobsChan) // Check for errors from workers for a := 1; a <= totalFiles; a++ { diff --git a/pkg/cmd/describe/describe.go b/pkg/cmd/describe/describe.go index e918debc..8c28b4bb 100644 --- a/pkg/cmd/describe/describe.go +++ b/pkg/cmd/describe/describe.go @@ -8,6 +8,7 @@ import ( edgeConnector "github.com/aziontech/azion-cli/pkg/cmd/describe/connector" function "github.com/aziontech/azion-cli/pkg/cmd/describe/function" functioninstance "github.com/aziontech/azion-cli/pkg/cmd/describe/function_instance" + networklist "github.com/aziontech/azion-cli/pkg/cmd/describe/network_list" origin "github.com/aziontech/azion-cli/pkg/cmd/describe/origin" "github.com/aziontech/azion-cli/pkg/cmd/describe/personal_token" ruleEngine "github.com/aziontech/azion-cli/pkg/cmd/describe/rules_engine" @@ -28,6 +29,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { $ azion describe application -h $ azion describe workload -h $ azion describe origin -h + $ azion describe network-list -h `), RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() @@ -45,6 +47,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(personal_token.NewCmd(f)) cmd.AddCommand(edgeConnector.NewCmd(f)) cmd.AddCommand(functioninstance.NewCmd(f)) + cmd.AddCommand(networklist.NewCmd(f)) cmd.Flags().BoolP("help", "h", false, msg.FlagHelp) return cmd diff --git a/pkg/cmd/describe/network_list/network_list.go b/pkg/cmd/describe/network_list/network_list.go new file mode 100644 index 00000000..2bf9f0c2 --- /dev/null +++ b/pkg/cmd/describe/network_list/network_list.go @@ -0,0 +1,105 @@ +package networklist + +import ( + "context" + "fmt" + "path/filepath" + + "github.com/MakeNowJust/heredoc" + msg "github.com/aziontech/azion-cli/messages/network_list" + api "github.com/aziontech/azion-cli/pkg/api/network_list" + "github.com/aziontech/azion-cli/pkg/cmdutil" + "github.com/aziontech/azion-cli/pkg/contracts" + "github.com/aziontech/azion-cli/pkg/iostreams" + "github.com/aziontech/azion-cli/pkg/output" + "github.com/aziontech/azion-cli/utils" + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" + "github.com/spf13/cobra" +) + +var ( + id string +) + +type DescribeCmd struct { + Io *iostreams.IOStreams + AskInput func(string) (string, error) + Get func(ctx context.Context, id string) (sdk.NetworkListDetail, error) +} + +func NewDescribeCmd(f *cmdutil.Factory) *DescribeCmd { + return &DescribeCmd{ + Io: f.IOStreams, + AskInput: func(prompt string) (string, error) { + return utils.AskInput(prompt) + }, + Get: func(ctx context.Context, id string) (sdk.NetworkListDetail, error) { + client := api.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + return client.Get(ctx, id) + }, + } +} + +func NewCobraCmd(describe *DescribeCmd, f *cmdutil.Factory) *cobra.Command { + opts := &contracts.DescribeOptions{} + cobraCmd := &cobra.Command{ + Use: msg.Usage, + Short: msg.DescribeShortDescription, + Long: msg.DescribeLongDescription, + SilenceUsage: true, + SilenceErrors: true, + Example: heredoc.Doc(` + $ azion describe network-list --network-list-id 4312 + $ azion describe network-list --network-list-id 1337 --with-code + $ azion describe network-list --network-list-id 1337 --out "./tmp/test.json" --format json + $ azion describe network-list --network-list-id 1337 --format json + `), + RunE: func(cmd *cobra.Command, args []string) error { + if !cmd.Flags().Changed("network-list-id") { + answer, err := describe.AskInput(msg.AskNetworkListID) + if err != nil { + return err + } + + id = answer + } + + ctx := context.Background() + resp, err := describe.Get(ctx, id) + if err != nil { + return fmt.Errorf(msg.ErrorGetNetworkList.Error(), err) + } + + fields := map[string]string{ + "Id": "ID", + "Name": "Name", + "Type": "Type", + "Items": "Items", + "LastEditor": "Last Editor", + "LastModified": "Last Modified", + "Active": "Active", + } + + describeOut := output.DescribeOutput{ + GeneralOutput: output.GeneralOutput{ + Out: f.IOStreams.Out, + Msg: filepath.Clean(opts.OutPath), + Flags: f.Flags, + }, + Fields: fields, + Values: &resp, + } + + return output.Print(&describeOut) + }, + } + + cobraCmd.Flags().StringVar(&id, "network-list-id", "", msg.FlagID) + cobraCmd.Flags().BoolP("help", "h", false, msg.DescribeHelpFlag) + + return cobraCmd +} + +func NewCmd(f *cmdutil.Factory) *cobra.Command { + return NewCobraCmd(NewDescribeCmd(f), f) +} diff --git a/pkg/cmd/describe/network_list/network_list_test.go b/pkg/cmd/describe/network_list/network_list_test.go new file mode 100644 index 00000000..e56db58d --- /dev/null +++ b/pkg/cmd/describe/network_list/network_list_test.go @@ -0,0 +1,86 @@ +package networklist + +import ( + "net/http" + "testing" + + "github.com/aziontech/azion-cli/pkg/httpmock" + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/pkg/testutils" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" +) + +var successResponse string = ` +{ + "data": { + "id": 1337, + "name": "My Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + +func TestDescribe(t *testing.T) { + logger.New(zapcore.DebugLevel) + + tests := []struct { + name string + request httpmock.Matcher + response httpmock.Responder + args []string + expectErr bool + mockInput func(string) (string, error) + }{ + { + name: "describe a network list", + request: httpmock.REST("GET", "workspace/network_lists/1337"), + response: httpmock.JSONFromString(successResponse), + args: []string{"--network-list-id", "1337"}, + expectErr: false, + }, + { + name: "describe a network list - no network list id", + request: httpmock.REST("GET", "workspace/network_lists/1337"), + response: httpmock.JSONFromString(successResponse), + expectErr: false, + mockInput: func(s string) (string, error) { + return "1337", nil + }, + }, + { + name: "not found", + request: httpmock.REST("GET", "workspace/network_lists/9999"), + response: httpmock.StatusStringResponse(http.StatusNotFound, "Not Found"), + args: []string{"--network-list-id", "9999"}, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mock := &httpmock.Registry{} + mock.Register(tt.request, tt.response) + + f, _, _ := testutils.NewFactory(mock) + descCmd := NewDescribeCmd(f) + descCmd.AskInput = tt.mockInput + cmd := NewCobraCmd(descCmd, f) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + if tt.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/cmd/dev/vulcan.go b/pkg/cmd/dev/vulcan.go index 3fa345fa..3dd28ca1 100644 --- a/pkg/cmd/dev/vulcan.go +++ b/pkg/cmd/dev/vulcan.go @@ -2,6 +2,7 @@ package dev import ( "fmt" + "strings" msg "github.com/aziontech/azion-cli/messages/dev" "github.com/aziontech/azion-cli/pkg/logger" @@ -11,17 +12,21 @@ import ( func vulcan(cmd *DevCmd, port int) error { vul := cmd.Vulcan() - command := vul.Command("", "dev", cmd.F) + baseCommand := vul.Command("", "dev", cmd.F) + + var commandBuilder strings.Builder + commandBuilder.WriteString(baseCommand) if port > 0 { - command = fmt.Sprintf("%s --port %d", command, port) + commandBuilder.WriteString(" --port ") + commandBuilder.WriteString(fmt.Sprintf("%d", port)) } if SkipFramework { - command = fmt.Sprintf("%s --skip-framework-build", command) + commandBuilder.WriteString(" --skip-framework-build") } - err := runCommand(cmd, command) + err := runCommand(cmd, commandBuilder.String()) if err != nil { return fmt.Errorf(msg.ErrorVulcanExecute.Error(), err.Error()) } diff --git a/pkg/cmd/init/init.go b/pkg/cmd/init/init.go index b9fb465c..32e7111d 100644 --- a/pkg/cmd/init/init.go +++ b/pkg/cmd/init/init.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/fs" - "log" "net/http" "os" "path" @@ -232,9 +231,8 @@ func (cmd *initCmd) Run(c *cobra.Command, _ []string) error { // Defer deletion of the temporary directory defer func() { - err := cmd.removeAll(tempDir) - if err != nil { - log.Fatal(err) + if cleanupErr := cmd.removeAll(tempDir); cleanupErr != nil { + logger.Debug("Failed to cleanup temporary directory", zap.Error(cleanupErr)) } }() diff --git a/pkg/cmd/init/init_test.go b/pkg/cmd/init/init_test.go index c47f8777..35fc5464 100644 --- a/pkg/cmd/init/init_test.go +++ b/pkg/cmd/init/init_test.go @@ -34,8 +34,6 @@ func TestNewCmd(t *testing.T) { NewCmd(f) } -var cloneOptions git.CloneOptions - func Test_initCmd_Run(t *testing.T) { logger.New(zapcore.DebugLevel) diff --git a/pkg/cmd/init/utils.go b/pkg/cmd/init/utils.go index 33e91f3d..53a31a7f 100644 --- a/pkg/cmd/init/utils.go +++ b/pkg/cmd/init/utils.go @@ -13,11 +13,6 @@ import ( "go.uber.org/zap" ) -var jsonTemplate = `{ - "scope": "global", - "preset": "%s" -}` - func (cmd *initCmd) askForInput(msg string, defaultIn string) (string, error) { var userInput string prompt := &survey.Input{ @@ -66,11 +61,12 @@ func (cmd *initCmd) selectVulcanTemplates(vul *vulcanPkg.VulcanPkg) error { return err } - if preset == strings.ToLower("vite") { - preset = "vue" + cmd.preset = strings.ToLower(preset) + + if cmd.preset == "vite" { + cmd.preset = "vue" } - cmd.preset = strings.ToLower(preset) return nil } diff --git a/pkg/cmd/link/link.go b/pkg/cmd/link/link.go index 4b8213cf..abf5cd54 100644 --- a/pkg/cmd/link/link.go +++ b/pkg/cmd/link/link.go @@ -27,6 +27,7 @@ import ( thoth "github.com/aziontech/go-thoth" gitlib "github.com/go-git/go-git/v5" "github.com/spf13/cobra" + "github.com/tidwall/gjson" "go.uber.org/zap" ) @@ -198,9 +199,22 @@ func (cmd *LinkCmd) run(c *cobra.Command, info *LinkInfo) error { } if info.Preset == "" { - err = cmd.selectVulcanMode(info) - if err != nil { - return err + infoJsonPath := filepath.Join(info.PathWorkingDir, "info.json") + if fileContent, err := cmd.FileReader(infoJsonPath); err == nil { + preset := gjson.Get(string(fileContent), "preset") + if preset.Exists() && preset.String() != "" { + info.Preset = preset.String() + logger.Debug("Preset loaded from info.json", zap.String("preset", info.Preset)) + logger.FInfoFlags(cmd.Io.Out, fmt.Sprintf(msg.PresetAutoDetected, info.Preset), cmd.F.Format, cmd.F.Out) + msgs = append(msgs, fmt.Sprintf(msg.PresetAutoDetected, info.Preset)) + } + } + + if info.Preset == "" { + err = cmd.selectVulcanMode(info) + if err != nil { + return err + } } } diff --git a/pkg/cmd/link/utils.go b/pkg/cmd/link/utils.go index c4bbbb53..edc59578 100644 --- a/pkg/cmd/link/utils.go +++ b/pkg/cmd/link/utils.go @@ -86,11 +86,9 @@ func (cmd *LinkCmd) selectVulcanMode(info *LinkInfo) error { return err } - // The list that comes from Vulcan has a blank line that we should remove. + // The list that comes from bundler has a blank line that we should remove. outputInline := strings.Split(output, "\n") - noLastItem := len(outputInline) - 1 - listPresets := make([]string, noLastItem) - copy(listPresets, outputInline[:noLastItem]) + listPresets := outputInline[:len(outputInline)-1] prompt := &survey.Select{ Message: "Choose a preset:", diff --git a/pkg/cmd/list/list.go b/pkg/cmd/list/list.go index b9ce6e31..1331af6e 100644 --- a/pkg/cmd/list/list.go +++ b/pkg/cmd/list/list.go @@ -8,6 +8,7 @@ import ( connector "github.com/aziontech/azion-cli/pkg/cmd/list/connector" function "github.com/aziontech/azion-cli/pkg/cmd/list/function" functioninstance "github.com/aziontech/azion-cli/pkg/cmd/list/function_instance" + networklist "github.com/aziontech/azion-cli/pkg/cmd/list/network_list" origin "github.com/aziontech/azion-cli/pkg/cmd/list/origin" token "github.com/aziontech/azion-cli/pkg/cmd/list/personal_token" rule "github.com/aziontech/azion-cli/pkg/cmd/list/rule_engine" @@ -30,6 +31,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { $ azion list workload -h $ azion list origin -h $ azion list function-instance -h + $ azion list network-list -h `), RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() @@ -48,6 +50,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(storage.NewCmd(f)) cmd.AddCommand(connector.NewCmd(f)) cmd.AddCommand(functioninstance.NewCmd(f)) + cmd.AddCommand(networklist.NewCmd(f)) cmd.Flags().BoolP("help", "h", false, msg.FlagHelp) return cmd diff --git a/pkg/cmd/list/network_list/fixtures/networklists.json b/pkg/cmd/list/network_list/fixtures/networklists.json new file mode 100644 index 00000000..7448508f --- /dev/null +++ b/pkg/cmd/list/network_list/fixtures/networklists.json @@ -0,0 +1,13 @@ +{ + "count": 1, + "results": [ + { + "id": 0, + "name": "string", + "type": "asn", + "last_editor": "string", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } + ] +} diff --git a/pkg/cmd/list/network_list/fixtures/nonetworklist.json b/pkg/cmd/list/network_list/fixtures/nonetworklist.json new file mode 100644 index 00000000..b377de77 --- /dev/null +++ b/pkg/cmd/list/network_list/fixtures/nonetworklist.json @@ -0,0 +1,4 @@ +{ + "count": 0, + "results": [] +} diff --git a/pkg/cmd/list/network_list/network_list.go b/pkg/cmd/list/network_list/network_list.go new file mode 100644 index 00000000..2e24d01f --- /dev/null +++ b/pkg/cmd/list/network_list/network_list.go @@ -0,0 +1,106 @@ +package networklist + +import ( + "context" + "fmt" + + "github.com/MakeNowJust/heredoc" + msg "github.com/aziontech/azion-cli/messages/network_list" + api "github.com/aziontech/azion-cli/pkg/api/network_list" + "github.com/aziontech/azion-cli/pkg/cmdutil" + "github.com/aziontech/azion-cli/pkg/contracts" + "github.com/aziontech/azion-cli/pkg/iostreams" + "github.com/aziontech/azion-cli/pkg/output" + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" + "github.com/spf13/cobra" +) + +type ListCmd struct { + Io *iostreams.IOStreams + ListNetworkList func(ctx context.Context, opts *contracts.ListOptions) (*sdk.PaginatedNetworkListList, error) +} + +func NewListCmd(f *cmdutil.Factory) *ListCmd { + return &ListCmd{ + Io: f.IOStreams, + ListNetworkList: func(ctx context.Context, opts *contracts.ListOptions) (*sdk.PaginatedNetworkListList, error) { + client := api.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + return client.List(ctx, opts) + }, + } +} + +func NewCobraCmd(list *ListCmd, f *cmdutil.Factory) *cobra.Command { + opts := &contracts.ListOptions{} + cmd := &cobra.Command{ + Use: msg.Usage, + Short: msg.ListShortDescription, + Long: msg.ListLongDescription, + SilenceUsage: true, + SilenceErrors: true, + Example: heredoc.Doc(` + $ azion list network-list --details + $ azion list network-list --order_by "id" + $ azion list network-list --page 1 + $ azion list network-list --page_size 5 + $ azion list network-list --sort "asc" + `), + RunE: func(cmd *cobra.Command, args []string) error { + if err := PrintTable(cmd, f, list, opts); err != nil { + return fmt.Errorf(msg.ErrorGetNetworkLists.Error(), err) + } + return nil + }, + } + + cmdutil.AddAzionApiFlags(cmd, opts) + cmd.Flags().BoolP("help", "h", false, msg.ListHelpFlag) + return cmd +} + +func PrintTable(cmd *cobra.Command, f *cmdutil.Factory, list *ListCmd, opts *contracts.ListOptions) error { + ctx := context.Background() + netlist, err := list.ListNetworkList(ctx, opts) + if err != nil { + return fmt.Errorf(msg.ErrorGetNetworkLists.Error(), err) + } + + listOut := output.ListOutput{} + listOut.Columns = []string{"ID", "NAME", "ACTIVE"} + listOut.Out = f.IOStreams.Out + listOut.Flags = f.Flags + + if opts.Details { + listOut.Columns = []string{"ID", "NAME", "ACTIVE", "TYPE"} + } + + if netlist == nil || len(netlist.Results) == 0 { + return output.Print(&listOut) + } + + for _, v := range netlist.Results { + var ln []string + if opts.Details { + ln = []string{ + fmt.Sprintf("%d", v.GetId()), + v.GetName(), + fmt.Sprintf("%v", v.GetActive()), + v.GetType(), + } + } else { + ln = []string{ + fmt.Sprintf("%d", v.GetId()), + v.GetName(), + fmt.Sprintf("%v", v.GetActive()), + } + } + listOut.Lines = append(listOut.Lines, ln) + } + + return output.Print(&listOut) +} + +func NewCmd(f *cmdutil.Factory) *cobra.Command { + listCmd := NewListCmd(f) + return NewCobraCmd(listCmd, f) +} diff --git a/pkg/cmd/list/network_list/network_list_test.go b/pkg/cmd/list/network_list/network_list_test.go new file mode 100644 index 00000000..ce00ad81 --- /dev/null +++ b/pkg/cmd/list/network_list/network_list_test.go @@ -0,0 +1,78 @@ +package networklist + +import ( + "testing" + + "github.com/aziontech/azion-cli/pkg/logger" + "go.uber.org/zap/zapcore" + + "github.com/aziontech/azion-cli/pkg/httpmock" + "github.com/aziontech/azion-cli/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + tblWithNetworkList string = "ID NAME ACTIVE \n0 string true \n" + tblNoNetworkList string = "ID NAME ACTIVE \n" +) + +func TestList(t *testing.T) { + logger.New(zapcore.DebugLevel) + t.Run("more than one network list", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("GET", "workspace/network_lists"), + httpmock.JSONFromFile("./fixtures/networklists.json"), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{}) + + _, err := cmd.ExecuteC() + require.NoError(t, err) + assert.Equal(t, tblWithNetworkList, stdout.String()) + }) + + t.Run("list - page 0 should generate an error", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("GET", "workspace/network_lists"), + httpmock.StatusStringResponse(404, "Not Found"), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--page", "0"}) + + _, err := cmd.ExecuteC() + require.Error(t, err) + }) + + t.Run("no network lists", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("GET", "workspace/network_lists"), + httpmock.JSONFromFile("./fixtures/nonetworklist.json"), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{}) + + _, err := cmd.ExecuteC() + require.NoError(t, err) + + assert.Equal(t, tblNoNetworkList, stdout.String()) + }) +} diff --git a/pkg/cmd/rollback/rollback.go b/pkg/cmd/rollback/rollback.go index 24930a4b..9b7ff6fb 100644 --- a/pkg/cmd/rollback/rollback.go +++ b/pkg/cmd/rollback/rollback.go @@ -6,19 +6,20 @@ import ( "github.com/MakeNowJust/heredoc" msg "github.com/aziontech/azion-cli/messages/rollback" - apiOrigin "github.com/aziontech/azion-cli/pkg/api/origin" + apiConnector "github.com/aziontech/azion-cli/pkg/api/connector" api "github.com/aziontech/azion-cli/pkg/api/storage" "github.com/aziontech/azion-cli/pkg/cmdutil" "github.com/aziontech/azion-cli/pkg/contracts" "github.com/aziontech/azion-cli/pkg/logger" "github.com/aziontech/azion-cli/pkg/output" "github.com/aziontech/azion-cli/utils" + sdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" "github.com/spf13/cobra" "go.uber.org/zap" ) var ( - originKey string + connectorID string projectPath string ) @@ -44,16 +45,16 @@ func NewCobraCmd(rollback *RollbackCmd, f *cmdutil.Factory) *cobra.Command { SilenceUsage: true, SilenceErrors: true, Example: heredoc.Doc(` - $ azion rollback --origin-key aaaa-bbbb-cccc-dddd + $ azion rollback --connector-id aaaa-bbbb-cccc-dddd `), RunE: func(cmd *cobra.Command, args []string) error { - if !cmd.Flags().Changed("origin-key") { - answer, err := rollback.AskInput(msg.ASKORIGIN) + if !cmd.Flags().Changed("connector-id") { + answer, err := rollback.AskInput(msg.ASKCONNECTOR) if err != nil { return err } - originKey = answer + connectorID = answer } conf, err := rollback.GetAzionJsonContent(projectPath) @@ -71,11 +72,25 @@ func NewCobraCmd(rollback *RollbackCmd, f *cmdutil.Factory) *cobra.Command { return msg.ERRORROLLBACK } - clientOrigin := apiOrigin.NewClient(f.HttpClient, f.Config.GetString("api_url"), f.Config.GetString("token")) - request := apiOrigin.UpdateRequest{} - request.SetPrefix(timestamp) + if timestamp == "" { + logger.Debug("No previous timestamp found for rollback") + return msg.ERRORNOPREVIOUS + } + + logger.Debug("Rolling back to previous timestamp", zap.String("from", conf.Prefix), zap.String("to", timestamp)) + + clientConnector := apiConnector.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + request := apiConnector.UpdateRequest{} + + attributes := sdk.ConnectorStorageAttributesRequest{} + attributes.SetBucket(conf.Bucket) + attributes.SetPrefix(timestamp) - _, err = clientOrigin.Update(context.Background(), conf.Application.ID, originKey, &request) + storageRequest := sdk.PatchedConnectorStorageRequest{} + storageRequest.SetAttributes(attributes) + request.PatchedConnectorStorageRequest = &storageRequest + + _, err = clientConnector.Update(context.Background(), &request, connectorID) if err != nil { return msg.ERRORROLLBACK } @@ -95,7 +110,7 @@ func NewCobraCmd(rollback *RollbackCmd, f *cmdutil.Factory) *cobra.Command { }, } - cobraCmd.Flags().StringVar(&originKey, "origin-key", "", msg.FLAGORIGINKEY) + cobraCmd.Flags().StringVar(&connectorID, "connector-id", "", msg.FLAGCONNECTORID) cobraCmd.Flags().StringVar(&projectPath, "config-dir", "azion", msg.CONFFLAG) cobraCmd.Flags().BoolP("help", "h", false, msg.FLAGHELP) @@ -110,29 +125,38 @@ func checkForNewTimestamp(f *cmdutil.Factory, referenceTimestamp, bucketName str logger.Debug("Checking if there are previous static files for the following bucket", zap.Any("Bucket name", bucketName)) client := api.NewClient(f.HttpClient, f.Config.GetString("storage_url"), f.Config.GetString("token")) c := context.Background() - options := &contracts.ListOptions{ - // Sort: "desc", - // OrderBy: "last_modified", - PageSize: 100000, - } - - resp, err := client.ListObject(c, bucketName, options) - if err != nil { - return "", err - } var prevTimestamp string - for _, object := range resp.Results { - parts := strings.Split(object.Key, "/") - if len(parts) > 1 { - timestamp := parts[0] - if timestamp == referenceTimestamp { - return prevTimestamp, nil - } else { + var continuationToken string + + for { + options := &contracts.ListOptions{ + ContinuationToken: continuationToken, + } + + resp, err := client.ListObject(c, bucketName, options) + if err != nil { + return "", err + } + + for _, object := range resp.Results { + parts := strings.Split(object.Key, "/") + if len(parts) > 1 { + timestamp := parts[0] + logger.Debug("Found timestamp in bucket", zap.String("timestamp", timestamp), zap.String("key", object.Key)) + if timestamp == referenceTimestamp { + logger.Debug("Found current timestamp, returning previous", zap.String("current", referenceTimestamp), zap.String("previous", prevTimestamp)) + return prevTimestamp, nil + } prevTimestamp = timestamp - continue } } + + logger.Debug("continuing to next page", zap.Any("continuation-token", resp.GetContinuationToken())) + if contToken, ok := resp.GetContinuationTokenOk(); contToken == nil || !ok { + break + } + continuationToken = resp.GetContinuationToken() } return referenceTimestamp, nil diff --git a/pkg/cmd/sync/tasks.go b/pkg/cmd/sync/tasks.go index 68e65d53..2e157c68 100644 --- a/pkg/cmd/sync/tasks.go +++ b/pkg/cmd/sync/tasks.go @@ -14,8 +14,10 @@ import ( "github.com/aziontech/azion-cli/pkg/cmdutil" "github.com/aziontech/azion-cli/pkg/contracts" "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/pkg/manifest" vulcanPkg "github.com/aziontech/azion-cli/pkg/vulcan" "github.com/aziontech/azion-cli/utils" + edgesdk "github.com/aziontech/azionapi-v4-go-sdk-dev/edge-api" "go.uber.org/zap" ) @@ -36,14 +38,42 @@ func SyncLocalResources(f *cmdutil.Factory, info contracts.SyncOpts, synch *Sync } var err error - manifest := &contracts.ManifestV4{} + var manifestStruct *contracts.ManifestV4 + var msgs []string - _, err = synch.syncCache(info, f) + interpreter := manifest.NewManifestInterpreter() + pathManifest, err := interpreter.ManifestPath() + if err != nil { + manifestStruct = &contracts.ManifestV4{ + Applications: []contracts.Applications{}, + Workloads: []contracts.WorkloadManifest{}, + WorkloadDeployments: []contracts.WorkloadDeployment{}, + Purge: []contracts.PurgeManifest{}, + Storage: []contracts.StorageManifest{}, + Functions: []contracts.Function{}, + Connectors: []edgesdk.ConnectorPolymorphicRequest{}, + } + } else { + manifestStruct, err = interpreter.ReadManifest(pathManifest, f, &msgs) + if err != nil { + manifestStruct = &contracts.ManifestV4{ + Applications: []contracts.Applications{}, + Workloads: []contracts.WorkloadManifest{}, + WorkloadDeployments: []contracts.WorkloadDeployment{}, + Purge: []contracts.PurgeManifest{}, + Storage: []contracts.StorageManifest{}, + Functions: []contracts.Function{}, + Connectors: []edgesdk.ConnectorPolymorphicRequest{}, + } + } + } + + _, err = synch.syncCache(info, f, manifestStruct) if err != nil { return fmt.Errorf(msg.ERRORSYNC, err.Error()) } - err = synch.syncRules(info, f, manifest) + err = synch.syncRules(info, f, manifestStruct) if err != nil { return fmt.Errorf(msg.ERRORSYNC, err.Error()) } @@ -58,7 +88,7 @@ func SyncLocalResources(f *cmdutil.Factory, info contracts.SyncOpts, synch *Sync return msg.INVALIDFORMAT } - err = synch.WriteManifest(manifest, "") + err = synch.WriteManifest(manifestStruct, "") if err != nil { return err } @@ -76,7 +106,7 @@ func SyncLocalResources(f *cmdutil.Factory, info contracts.SyncOpts, synch *Sync return nil } -func (synch *SyncCmd) syncCache(info contracts.SyncOpts, f *cmdutil.Factory) (map[string]contracts.AzionJsonDataCacheSettings, error) { +func (synch *SyncCmd) syncCache(info contracts.SyncOpts, f *cmdutil.Factory, manifest *contracts.ManifestV4) (map[string]contracts.AzionJsonDataCacheSettings, error) { remoteCacheIds := make(map[string]contracts.AzionJsonDataCacheSettings) client := edgeApp.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) str := strconv.FormatInt(info.Conf.Application.ID, 10) @@ -85,21 +115,74 @@ func (synch *SyncCmd) syncCache(info contracts.SyncOpts, f *cmdutil.Factory) (ma return remoteCacheIds, err } - cacheAzion := []contracts.AzionJsonDataCacheSettings{} - info.Conf.CacheSettings = cacheAzion + if len(manifest.Applications) == 0 { + appName := info.Conf.Application.Name + if appName == "" || appName == "__DEFAULT__" { + appName = info.Conf.Name + } + manifest.Applications = append(manifest.Applications, contracts.Applications{ + Name: appName, + Rules: []contracts.ManifestRulesEngine{}, + CacheSettings: []contracts.ManifestCacheSetting{}, + }) + } else if manifest.Applications[0].CacheSettings == nil { + manifest.Applications[0].CacheSettings = []contracts.ManifestCacheSetting{} + } + + cacheAzion := info.Conf.CacheSettings + existingCacheNames := make(map[string]bool) + for _, existingCache := range cacheAzion { + existingCacheNames[existingCache.Name] = true + } + for _, cache := range resp { remoteCacheIds[strconv.FormatInt(cache.Id, 10)] = contracts.AzionJsonDataCacheSettings{ Id: cache.Id, Name: cache.Name, } - newCache := contracts.AzionJsonDataCacheSettings{ - Id: cache.GetId(), - Name: cache.GetName(), + if !existingCacheNames[cache.GetName()] { + newCache := contracts.AzionJsonDataCacheSettings{ + Id: cache.GetId(), + Name: cache.GetName(), + } + cacheAzion = append(cacheAzion, newCache) + existingCacheNames[cache.GetName()] = true + + cacheManifest := contracts.ManifestCacheSetting{ + Name: cache.GetName(), + } + + browserCache := cache.GetBrowserCache() + browserCacheBytes, err := json.Marshal(browserCache) + if err != nil { + return remoteCacheIds, err + } + var browserCacheRequest edgesdk.BrowserCacheModuleRequest + err = json.Unmarshal(browserCacheBytes, &browserCacheRequest) + if err != nil { + return remoteCacheIds, err + } + cacheManifest.BrowserCache = &browserCacheRequest + + // Convert Modules from response type to request type via JSON + modules := cache.GetModules() + modulesBytes, err := json.Marshal(modules) + if err != nil { + return remoteCacheIds, err + } + var modulesRequest edgesdk.CacheSettingsModulesRequest + err = json.Unmarshal(modulesBytes, &modulesRequest) + if err != nil { + return remoteCacheIds, err + } + cacheManifest.Modules = &modulesRequest + + manifest.Applications[0].CacheSettings = append(manifest.Applications[0].CacheSettings, cacheManifest) } - cacheAzion = append(cacheAzion, newCache) - info.Conf.CacheSettings = cacheAzion } + + info.Conf.CacheSettings = cacheAzion err = utils.WriteAzionJsonContentPreserveOrder(info.Conf, ProjectConf) if err != nil { logger.Debug("Error while writing azion.json file", zap.Error(err)) @@ -111,14 +194,22 @@ func (synch *SyncCmd) syncCache(info contracts.SyncOpts, f *cmdutil.Factory) (ma func (synch *SyncCmd) syncRules(info contracts.SyncOpts, f *cmdutil.Factory, manifest *contracts.ManifestV4) error { client := edgeApp.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) str := strconv.FormatInt(info.Conf.Application.ID, 10) - rulesAzion := []contracts.AzionJsonDataRules{} - info.Conf.RulesEngine.Rules = rulesAzion + rulesAzion := info.Conf.RulesEngine.Rules + existingRuleNames := make(map[string]bool) + for _, existingRule := range rulesAzion { + existingRuleNames[existingRule.Name] = true + } // Initialize Applications slice if it's empty if len(manifest.Applications) == 0 { + appName := info.Conf.Application.Name + if appName == "" || appName == "__DEFAULT__" { + appName = info.Conf.Name + } manifest.Applications = append(manifest.Applications, contracts.Applications{ - Name: info.Conf.Application.Name, - Rules: []contracts.ManifestRulesEngine{}, + Name: appName, + Rules: []contracts.ManifestRulesEngine{}, + CacheSettings: []contracts.ManifestCacheSetting{}, }) } @@ -135,30 +226,55 @@ func (synch *SyncCmd) syncRules(info contracts.SyncOpts, f *cmdutil.Factory, man continue } - manifestRule := contracts.ManifestRulesEngine{ - Phase: "request", - Rule: contracts.ManifestRule{ - Name: rule.GetName(), - Description: rule.GetDescription(), - Active: rule.GetActive(), - // Convert criteria and behaviors to appropriate types - }, + var criteria [][]edgesdk.EdgeApplicationCriterionFieldRequest + criteriaBytes, err := json.Marshal(rule.Criteria) + if err != nil { + return fmt.Errorf(msg.ERRORMARSHALCRITERIA, err) + } + err = json.Unmarshal(criteriaBytes, &criteria) + if err != nil { + return fmt.Errorf(msg.ERRORUNMARSHALCRITERIA, err) + } + + var behaviors []contracts.ManifestRuleBehavior + behaviorsBytes, err := json.Marshal(rule.Behaviors) + if err != nil { + return fmt.Errorf(msg.ERRORMARSHALBEHAVIORS, err) + } + err = json.Unmarshal(behaviorsBytes, &behaviors) + if err != nil { + return fmt.Errorf(msg.ERRORUNMARSHALBEHAVIORS, err) } - manifest.Applications[0].Rules = append(manifest.Applications[0].Rules, manifestRule) + // Only add rule if it doesn't already exist locally + if !existingRuleNames[rule.GetName()] { + manifestRule := contracts.ManifestRulesEngine{ + Phase: "request", + Rule: contracts.ManifestRule{ + Name: rule.GetName(), + Description: rule.GetDescription(), + Active: rule.GetActive(), + Criteria: criteria, + Behaviors: behaviors, + }, + } + + manifest.Applications[0].Rules = append(manifest.Applications[0].Rules, manifestRule) - newRule := contracts.AzionJsonDataRules{ - Id: rule.GetId(), - Name: rule.GetName(), - Phase: "request", + newRule := contracts.AzionJsonDataRules{ + Id: rule.GetId(), + Name: rule.GetName(), + Phase: "request", + } + rulesAzion = append(rulesAzion, newRule) + existingRuleNames[rule.GetName()] = true } - rulesAzion = append(rulesAzion, newRule) } respResp, err := client.ListRulesEngineResponse(context.Background(), opts, str) if err != nil { logger.Debug("Error while listing response phase rules", zap.Error(err)) - return fmt.Errorf("failed to list response phase rules: %w", err) + return fmt.Errorf(msg.ERRORLISTRESPONSERULES, err) } for _, rule := range respResp.Results { @@ -167,24 +283,48 @@ func (synch *SyncCmd) syncRules(info contracts.SyncOpts, f *cmdutil.Factory, man continue } - manifestRule := contracts.ManifestRulesEngine{ - Phase: "response", - Rule: contracts.ManifestRule{ - Name: rule.GetName(), - Description: rule.GetDescription(), - Active: rule.GetActive(), - // Convert criteria and behaviors to appropriate types - }, + var criteria [][]edgesdk.EdgeApplicationCriterionFieldRequest + criteriaBytes, err := json.Marshal(rule.Criteria) + if err != nil { + return fmt.Errorf(msg.ERRORMARSHALCRITERIA, err) + } + err = json.Unmarshal(criteriaBytes, &criteria) + if err != nil { + return fmt.Errorf(msg.ERRORUNMARSHALCRITERIA, err) + } + + var behaviors []contracts.ManifestRuleBehavior + behaviorsBytes, err := json.Marshal(rule.Behaviors) + if err != nil { + return fmt.Errorf(msg.ERRORMARSHALBEHAVIORS, err) + } + err = json.Unmarshal(behaviorsBytes, &behaviors) + if err != nil { + return fmt.Errorf(msg.ERRORUNMARSHALBEHAVIORS, err) } - manifest.Applications[0].Rules = append(manifest.Applications[0].Rules, manifestRule) + if !existingRuleNames[rule.GetName()] { + manifestRule := contracts.ManifestRulesEngine{ + Phase: "response", + Rule: contracts.ManifestRule{ + Name: rule.GetName(), + Description: rule.GetDescription(), + Active: rule.GetActive(), + Criteria: criteria, + Behaviors: behaviors, + }, + } + + manifest.Applications[0].Rules = append(manifest.Applications[0].Rules, manifestRule) - newRule := contracts.AzionJsonDataRules{ - Id: rule.GetId(), - Name: rule.GetName(), - Phase: "response", + newRule := contracts.AzionJsonDataRules{ + Id: rule.GetId(), + Name: rule.GetName(), + Phase: "response", + } + rulesAzion = append(rulesAzion, newRule) + existingRuleNames[rule.GetName()] = true } - rulesAzion = append(rulesAzion, newRule) } // Update the configuration with all rules @@ -210,7 +350,7 @@ func (synch *SyncCmd) syncEnv(f *cmdutil.Factory) error { envs, err := synch.ReadEnv(synch.EnvPath) if err != nil { logger.Debug("Error while loading .env file", zap.Error(err)) - return nil // not every project has a .env file... this should not stop the execution + return nil // not every project has a .env file; this should not stop the execution } for _, variable := range resp { diff --git a/pkg/cmd/update/cache_setting/cache_setting.go b/pkg/cmd/update/cache_setting/cache_setting.go index 0af8efba..a73ed8d8 100644 --- a/pkg/cmd/update/cache_setting/cache_setting.go +++ b/pkg/cmd/update/cache_setting/cache_setting.go @@ -23,24 +23,20 @@ import ( ) type Fields struct { - ApplicationID int64 - CacheSettingID int64 - Name string - browserCacheSettings string - browserCacheBehavior string - browserCacheMaxAge int64 - browserCacheSettingsMaximumTtl int64 - cdnCacheSettings string - cdnCacheSettingsMaximumTtl int64 - cacheByQueryString string - queryStringFields []string - enableQueryStringSort string - cacheByCookies string - cookieNames []string - enableCachingForPost string - enableCachingForOptions string - l2CachingEnabled string - Path string + ApplicationID int64 + CacheSettingID int64 + Name string + browserCacheSettings string + browserCacheBehavior string + browserCacheMaxAge int64 + cacheByQueryString string + queryStringFields []string + enableQueryStringSort string + cacheByCookies string + cookieNames []string + enableCachingForPost string + enableCachingForOptions string + Path string } func NewCmd(f *cmdutil.Factory) *cobra.Command { diff --git a/pkg/cmd/update/network_list/fixtures/update.json b/pkg/cmd/update/network_list/fixtures/update.json new file mode 100644 index 00000000..ab1482e3 --- /dev/null +++ b/pkg/cmd/update/network_list/fixtures/update.json @@ -0,0 +1,6 @@ +{ + "name": "Updated via File", + "type": "ip_cidr", + "items": ["192.168.1.0/24", "10.0.0.0/8"], + "active": true +} diff --git a/pkg/cmd/update/network_list/network_list.go b/pkg/cmd/update/network_list/network_list.go new file mode 100644 index 00000000..36f95e88 --- /dev/null +++ b/pkg/cmd/update/network_list/network_list.go @@ -0,0 +1,191 @@ +package networklist + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/MakeNowJust/heredoc" + msg "github.com/aziontech/azion-cli/messages/network_list" + api "github.com/aziontech/azion-cli/pkg/api/network_list" + "github.com/aziontech/azion-cli/pkg/cmdutil" + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/pkg/output" + "github.com/aziontech/azion-cli/utils" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "go.uber.org/zap" +) + +type Fields struct { + ID string + Name string + Type string + Items string + AddItem string + RemoveItem string + Active string + InPath string +} + +func NewCmd(f *cmdutil.Factory) *cobra.Command { + fields := &Fields{} + + cmd := &cobra.Command{ + Use: msg.Usage, + Short: msg.UpdateShortDescription, + Long: msg.UpdateLongDescription, + SilenceUsage: true, + SilenceErrors: true, + Example: heredoc.Doc(` + $ azion update network-list --network-list-id 1234 --name "Updated List" + $ azion update network-list --network-list-id 4185 --type ip_cidr --items "192.168.1.0/24,10.0.0.0/8" + $ azion update network-list --network-list-id 9123 --active true + $ azion update network-list --network-list-id 9123 --active false + $ azion update network-list --network-list-id 1 --add-item "1.1.1.1" + $ azion update network-list --network-list-id 1 --add-item "1.1.1.1,2.2.2.2,3.3.3.3" + $ azion update network-list --network-list-id 1 --remove-item "1.1.1.1" + $ azion update network-list --network-list-id 1 --remove-item "1.1.1.1,2.2.2.2" + $ azion update network-list --file "update.json" + `), + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("network-list-id") { + answer, err := utils.AskInput(msg.UpdateAskNetworkListID) + + if err != nil { + logger.Debug("Error while parsing answer", zap.Error(err)) + return utils.ErrorParseResponse + } + + fields.ID = answer + } + + request := api.UpdateRequest{} + + client := api.NewClient(f.HttpClient, f.Config.GetString("api_v4_url"), f.Config.GetString("token")) + ctx := context.Background() + + if cmd.Flags().Changed("file") { + err := utils.FlagFileUnmarshalJSON(fields.InPath, &request) + if err != nil { + return utils.ErrorUnmarshalReader + } + } else { + if cmd.Flags().Changed("add-item") || cmd.Flags().Changed("remove-item") { + current, err := client.Get(ctx, fields.ID) + if err != nil { + return fmt.Errorf(msg.ErrorGetNetworkList.Error(), err) + } + + currentItems := current.GetItems() + modifiedItems := make([]string, len(currentItems)) + copy(modifiedItems, currentItems) + + if cmd.Flags().Changed("add-item") { + for newItem := range strings.SplitSeq(fields.AddItem, ",") { + newItem = strings.TrimSpace(newItem) + if newItem == "" { + continue + } + + exists := false + for _, item := range modifiedItems { + if item == newItem { + exists = true + break + } + } + if !exists { + modifiedItems = append(modifiedItems, newItem) + } + } + } + + if cmd.Flags().Changed("remove-item") { + removeMap := make(map[string]bool) + for removeItem := range strings.SplitSeq(fields.RemoveItem, ",") { + removeItem = strings.TrimSpace(removeItem) + if removeItem != "" { + removeMap[removeItem] = true + } + } + filteredItems := []string{} + for _, item := range modifiedItems { + if !removeMap[item] { + filteredItems = append(filteredItems, item) + } + } + modifiedItems = filteredItems + } + + request.SetItems(modifiedItems) + } + + err := createRequestFromFlags(cmd, fields, &request) + if err != nil { + return err + } + } + + response, err := client.Update(ctx, &request, fields.ID) + + if err != nil { + return fmt.Errorf(msg.ErrorUpdateNetworkList.Error(), err) + } + + updateOut := output.GeneralOutput{ + Msg: fmt.Sprintf(msg.UpdateOutputSuccess, response.GetId()), + Out: f.IOStreams.Out, + Flags: f.Flags, + } + return output.Print(&updateOut) + }, + } + + flags := cmd.Flags() + addFlags(flags, fields) + + return cmd +} + +func createRequestFromFlags(cmd *cobra.Command, fields *Fields, request *api.UpdateRequest) error { + if cmd.Flags().Changed("name") { + request.SetName(fields.Name) + } + + if cmd.Flags().Changed("type") { + request.SetType(fields.Type) + } + + if cmd.Flags().Changed("items") { + items := strings.Split(fields.Items, ",") + for i := range items { + items[i] = strings.TrimSpace(items[i]) + } + request.SetItems(items) + } + + if cmd.Flags().Changed("active") { + active, err := strconv.ParseBool(fields.Active) + if err != nil { + return fmt.Errorf("%w: %q", msg.ErrorActiveFlag, fields.Active) + } + request.SetActive(active) + } + + return nil +} + +func addFlags(flags *pflag.FlagSet, fields *Fields) { + flags.StringVar(&fields.ID, "network-list-id", "", msg.FlagID) + flags.StringVar(&fields.Name, "name", "", msg.FlagName) + flags.StringVar(&fields.Type, "type", "", msg.FlagType) + flags.StringVar(&fields.Items, "items", "", msg.FlagItems) + flags.StringVar(&fields.AddItem, "add-item", "", msg.FlagAddItem) + flags.StringVar(&fields.RemoveItem, "remove-item", "", msg.FlagRemoveItem) + flags.StringVar(&fields.Active, "active", "", msg.FlagActive) + flags.StringVar(&fields.InPath, "file", "", msg.FlagIn) + flags.BoolP("help", "h", false, msg.UpdateHelpFlag) +} diff --git a/pkg/cmd/update/network_list/network_list_test.go b/pkg/cmd/update/network_list/network_list_test.go new file mode 100644 index 00000000..177cf9ed --- /dev/null +++ b/pkg/cmd/update/network_list/network_list_test.go @@ -0,0 +1,420 @@ +package networklist + +import ( + "fmt" + "net/http" + "testing" + + "github.com/aziontech/azion-cli/pkg/logger" + "go.uber.org/zap/zapcore" + + msg "github.com/aziontech/azion-cli/messages/network_list" + "github.com/aziontech/azion-cli/pkg/httpmock" + "github.com/aziontech/azion-cli/pkg/testutils" + "github.com/stretchr/testify/require" +) + +var successResponse string = ` +{ + "data": { + "id": 1337, + "name": "Updated Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + +func TestUpdate(t *testing.T) { + logger.New(zapcore.DebugLevel) + t.Run("update Network List name and active", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--name", "Updated Network List", "--active", "true"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("update type and items", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--type", "ip_cidr", "--items", "192.168.1.0/24,10.0.0.0/8"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("update only name", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--name", "Updated Network List"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("update only active status", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--active", "false"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("bad request - invalid active value", func(t *testing.T) { + mock := &httpmock.Registry{} + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1234"), + httpmock.StatusStringResponse(http.StatusBadRequest, `{"details": "invalid field active"}`), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1234", "--active", "invalid"}) + + err := cmd.Execute() + + require.Error(t, err) + }) + + t.Run("update with file", func(t *testing.T) { + mock := &httpmock.Registry{} + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--file", "./fixtures/update.json"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("not found", func(t *testing.T) { + mock := &httpmock.Registry{} + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/9999"), + httpmock.StatusStringResponse(http.StatusNotFound, `{"details": "Network List not found"}`), + ) + + f, _, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "9999", "--name", "Test"}) + + err := cmd.Execute() + + require.Error(t, err) + }) + + t.Run("add single item", func(t *testing.T) { + mock := &httpmock.Registry{} + + // Mock GET to retrieve current items + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--add-item", "203.0.113.0/24"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("add multiple items", func(t *testing.T) { + mock := &httpmock.Registry{} + + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--add-item", "203.0.113.0/24,172.16.0.0/12"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("remove single item", func(t *testing.T) { + mock := &httpmock.Registry{} + + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8", + "203.0.113.0/24" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--remove-item", "10.0.0.0/8"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("remove multiple items", func(t *testing.T) { + mock := &httpmock.Registry{} + + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8", + "203.0.113.0/24", + "172.16.0.0/12" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--remove-item", "10.0.0.0/8,172.16.0.0/12"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("add and remove items together", func(t *testing.T) { + mock := &httpmock.Registry{} + + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--add-item", "203.0.113.0/24", "--remove-item", "10.0.0.0/8"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) + + t.Run("add item that already exists", func(t *testing.T) { + mock := &httpmock.Registry{} + + getCurrentResponse := ` +{ + "data": { + "id": 1337, + "name": "Test Network List", + "type": "ip_cidr", + "items": [ + "192.168.1.0/24", + "10.0.0.0/8" + ], + "last_editor": "user@example.com", + "last_modified": "2019-08-24T14:15:22Z", + "active": true + } +} +` + mock.Register( + httpmock.REST("GET", "workspace/network_lists/1337"), + httpmock.JSONFromString(getCurrentResponse), + ) + + mock.Register( + httpmock.REST("PATCH", "workspace/network_lists/1337"), + httpmock.JSONFromString(successResponse), + ) + + f, stdout, _ := testutils.NewFactory(mock) + + cmd := NewCmd(f) + + cmd.SetArgs([]string{"--network-list-id", "1337", "--add-item", "192.168.1.0/24"}) + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, fmt.Sprintf(msg.UpdateOutputSuccess, 1337), stdout.String()) + }) +} diff --git a/pkg/cmd/update/update.go b/pkg/cmd/update/update.go index 83ad1226..ac5936d1 100644 --- a/pkg/cmd/update/update.go +++ b/pkg/cmd/update/update.go @@ -8,6 +8,7 @@ import ( connector "github.com/aziontech/azion-cli/pkg/cmd/update/connector" function "github.com/aziontech/azion-cli/pkg/cmd/update/function" functionInstance "github.com/aziontech/azion-cli/pkg/cmd/update/function_instance" + networkList "github.com/aziontech/azion-cli/pkg/cmd/update/network_list" origin "github.com/aziontech/azion-cli/pkg/cmd/update/origin" rulesEngine "github.com/aziontech/azion-cli/pkg/cmd/update/rules_engine" storage "github.com/aziontech/azion-cli/pkg/cmd/update/storage" @@ -27,6 +28,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { $ azion update application -h $ azion update connector -h $ azion update workload -h + $ azion update network-list -h `), RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() @@ -43,6 +45,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(workloads.NewCmd(f)) cmd.AddCommand(connector.NewCmd(f)) cmd.AddCommand(functionInstance.NewCmd(f)) + cmd.AddCommand(networkList.NewCmd(f)) cmd.Flags().BoolP("help", "h", false, msg.FlagHelp) return cmd diff --git a/pkg/command/command.go b/pkg/command/command.go index aee4834f..ec3f5488 100644 --- a/pkg/command/command.go +++ b/pkg/command/command.go @@ -68,7 +68,7 @@ func CommandRunInteractive(f *cmdutil.Factory, comm string) error { return cmd.Run() } -// RunCommandStreamOutput executes the provived command while streaming its logs (stdout+stderr) directly to terminal +// RunCommandStreamOutput executes the provided command while streaming its logs (stdout+stderr) directly to terminal func RunCommandStreamOutput(out io.Writer, envVars []string, comm string) error { command := exec.Command(SHELL, "-c", comm) if len(envVars) > 0 { @@ -97,7 +97,7 @@ func RunCommandStreamOutput(out io.Writer, envVars []string, comm string) error in := bufio.NewScanner(multi) for in.Scan() { - fmt.Fprintf(out, "%s\n", in.Text()) + fmt.Fprintln(out, in.Text()) } if err := in.Err(); err != nil { return fmt.Errorf(utils.ErrorRunningCommandStream.Error(), err) diff --git a/pkg/command/command_windows.go b/pkg/command/command_windows.go index beabbb7d..16af613e 100644 --- a/pkg/command/command_windows.go +++ b/pkg/command/command_windows.go @@ -46,7 +46,7 @@ func RunCommandStreamOutput(out io.Writer, envVars []string, comm string) error in := bufio.NewScanner(multi) for in.Scan() { - fmt.Fprintf(out, "%s\n", in.Text()) + fmt.Fprintln(out, in.Text()) } // Check for scanning errors diff --git a/pkg/contracts/contracts.go b/pkg/contracts/contracts.go index aa089c7f..8cc32c47 100644 --- a/pkg/contracts/contracts.go +++ b/pkg/contracts/contracts.go @@ -32,10 +32,6 @@ type BuildInfoV3 struct { IsFirewall bool } -type DevInfo struct { - IsFirewall string -} - type ListOptions struct { Details bool OrderBy string @@ -147,37 +143,6 @@ type AzionApplicationSimple struct { Application AzionJsonDataApplication `json:"application"` } -type AzionApplicationConfig struct { - InitData InitConf `json:"init"` - BuildData BuildConf `json:"build"` - PublishData PublishConf `json:"publish"` -} - -type InitConf struct { - Cmd string `json:"cmd"` - Env string `json:"env"` - OutputCtrl string `json:"output-ctrl"` - Default string `json:"default"` -} - -type BuildConf struct { - Cmd string `json:"cmd"` - Env string `json:"env"` - OutputCtrl string `json:"output-ctrl"` - Default string `json:"default"` -} - -type PublishConf struct { - Cmd string `json:"pre_cmd"` - Env string `json:"env"` - OutputCtrl string `json:"output-ctrl"` - Default string `json:"default"` -} - -type CacheConf struct { - PurgeOnPublish bool `json:"purge_on_publish"` -} - type AzionJsonDataFunction struct { ID int64 `json:"id"` Name string `json:"name"` @@ -248,19 +213,22 @@ type Manifest struct { Purge []Purges `json:"purge"` } -// BuildManifest represents the build configuration in the manifest.json file -type BuildManifest struct { - Build Build `json:"build,omitempty"` +type Build struct { + Preset string `json:"preset,omitempty"` + Entry []string `json:"entry,omitempty"` + Polyfills bool `json:"polyfills,omitempty"` + Bundler string `json:"bundler,omitempty"` + Worker bool `json:"worker,omitempty"` + MemoryFS *MemoryFS `json:"memoryFS,omitempty"` } -type Build struct { - Preset string `json:"preset,omitempty"` // JavaScript, etc. - Entry []string `json:"entry,omitempty"` // Entry files like main.js - Polyfills bool `json:"polyfills,omitempty"` // Whether to include polyfills +type MemoryFS struct { + InjectionDirs []string `json:"injectionDirs,omitempty"` + RemovePathPrefix string `json:"removePathPrefix,omitempty"` } type ManifestV4 struct { - Build BuildManifest `json:"build"` + Build Build `json:"build"` Storage []StorageManifest `json:"storage"` Functions []Function `json:"functions"` Applications []Applications `json:"applications"` @@ -282,7 +250,7 @@ type WorkloadManifest struct { Active *bool `json:"active,omitempty"` Infrastructure int64 `json:"infrastructure,omitempty"` WorkloadDomainAllowAccess *bool `json:"workload_domain_allow_access,omitempty"` - Domains []string `json:"domains,omitempty"` + Domains []string `json:"domains"` Tls *edgesdk.TLSWorkloadRequest `json:"tls,omitempty"` Protocols *edgesdk.ProtocolsRequest `json:"protocols,omitempty"` Mtls *edgesdk.MTLSRequest `json:"mtls,omitempty"` @@ -407,60 +375,6 @@ type ManifestCacheSetting struct { Modules *edgesdk.CacheSettingsModulesRequest `json:"modules,omitempty"` } -type ManifestBrowserCache struct { - Behavior string `json:"behavior,omitempty"` - MaxAge int64 `json:"max_age,omitempty"` -} - -type ManifestCacheModules struct { - EdgeCache *ManifestEdgeCache `json:"edge_cache,omitempty"` - TieredCache *ManifestTieredCache `json:"tiered_cache,omitempty"` - ApplicationAccelerator *ManifestAppAccelerator `json:"application_accelerator,omitempty"` -} - -type ManifestEdgeCache struct { - Behavior string `json:"behavior,omitempty"` - MaxAge int64 `json:"max_age,omitempty"` - StaleCache *ManifestStaleCache `json:"stale_cache,omitempty"` - LargeFileCache *ManifestLargeFileCache `json:"large_file_cache,omitempty"` -} - -type ManifestStaleCache struct { - Enabled bool `json:"enabled"` -} - -type ManifestLargeFileCache struct { - Enabled bool `json:"enabled"` - Offset int64 `json:"offset,omitempty"` -} - -type ManifestTieredCache struct { - Topology string `json:"topology,omitempty"` -} - -type ManifestAppAccelerator struct { - CacheVaryByMethod []string `json:"cache_vary_by_method,omitempty"` - CacheVaryByQuerystring *ManifestQuerystringCache `json:"cache_vary_by_querystring,omitempty"` - CacheVaryByCookies *ManifestCookiesCache `json:"cache_vary_by_cookies,omitempty"` - CacheVaryByDevices *ManifestDevicesCache `json:"cache_vary_by_devices,omitempty"` -} - -type ManifestQuerystringCache struct { - Behavior string `json:"behavior,omitempty"` - Fields []string `json:"fields,omitempty"` - SortEnabled bool `json:"sort_enabled"` -} - -type ManifestCookiesCache struct { - Behavior string `json:"behavior,omitempty"` - CookieNames []string `json:"cookie_names,omitempty"` -} - -type ManifestDevicesCache struct { - Behavior string `json:"behavior,omitempty"` - DeviceGroup []string `json:"device_group,omitempty"` -} - type CacheSettingManifest struct { Name string `json:"name"` BrowserCache *BrowserCacheSettings `json:"browser_cache,omitempty"` @@ -537,14 +451,6 @@ type StorageManifest struct { Prefix string `json:"prefix"` } -// WorkloadHttp represents HTTP configuration for a workload -type WorkloadHttp struct { - Versions []string `json:"versions,omitempty"` - HttpPorts []int64 `json:"http_ports,omitempty"` - HttpsPorts []int64 `json:"https_ports,omitempty"` - QuicPorts []int64 `json:"quic_ports,omitempty"` -} - type Applications struct { Name string `json:"name"` Modules *edgesdk.ApplicationModulesRequest `json:"modules,omitempty"` @@ -576,22 +482,6 @@ type Function struct { Argument string `json:"argument,omitempty"` } -type Bindings struct { - Storage Storage `json:"storage"` -} - -type Storage struct { - Bucket string `json:"bucket"` - Prefix string `json:"prefix"` -} - -type BuildConfig struct { - Preset string `json:"preset"` - Entry []string `json:"entry"` - Polyfills bool `json:"polyfills"` - Worker bool `json:"worker"` -} - type Modules struct { EdgeCacheEnabled *bool `json:"edge_cache_enabled,omitempty"` EdgeFunctionsEnabled *bool `json:"edge_functions_enabled,omitempty"` @@ -615,21 +505,8 @@ type ManifestRule struct { Behaviors []ManifestRuleBehavior `json:"behaviors"` } -// ManifestCriteria represents a criteria in a rule -type ManifestCriteria struct { - Variable string `json:"variable"` - Conditional string `json:"conditional,omitempty"` - Operator string `json:"operator"` - Argument string `json:"argument"` -} - // ManifestRuleBehavior represents a behavior in a rule type ManifestRuleBehavior struct { - Type string `json:"type"` + Type string `json:"type,omitempty"` Attributes map[string]interface{} `json:"attributes,omitempty"` } - -type EdgeConnectorManifest struct { - Name string `json:"name"` - Address []string `json:"address"` -} diff --git a/pkg/github/github.go b/pkg/github/github.go index 8f353ce2..2fcf7268 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -34,10 +34,6 @@ type Release struct { PublishedAt string `json:"published_at"` } -var ( - ApiURL string -) - func NewGithub() *Github { return &Github{ GetVersionGitHub: getVersionGitHub, @@ -51,9 +47,9 @@ func NewGithub() *Github { } func getVersionGitHub(name string) (string, string, error) { - ApiURL = fmt.Sprintf("https://api.github.com/repos/aziontech/%s/releases/latest", name) + apiURL := fmt.Sprintf("https://api.github.com/repos/aziontech/%s/releases/latest", name) - response, err := http.Get(ApiURL) + response, err := http.Get(apiURL) if err != nil { logger.Debug("Failed to get latest version of "+name, zap.Error(err)) return "", "", err @@ -136,8 +132,7 @@ func writeGitignore(path string) error { writer := bufio.NewWriter(file) for _, line := range linesToAdd { - _, err := writer.WriteString(line + "\n") - if err != nil { + if _, err := fmt.Fprintln(writer, line); err != nil { logger.Error("Error writing to .gitignore file", zap.Error(err)) return err } diff --git a/pkg/github/github_test.go b/pkg/github/github_test.go index 84c2481f..5b47b262 100644 --- a/pkg/github/github_test.go +++ b/pkg/github/github_test.go @@ -53,10 +53,6 @@ func TestGetVersionGitHub(t *testing.T) { })) defer server.Close() - oldURL := ApiURL - ApiURL = server.URL + "/repos/aziontech/%s/releases/tag/1.30.0" - defer func() { ApiURL = oldURL }() - gh := NewGithub() gh.GetVersionGitHub = func(name string) (string, string, error) { return tt.wantTag, "2025-09-12T19:15:10Z", nil diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 7446ab0b..93dcab1b 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -26,9 +26,9 @@ func LogLevel(logger Logger) { New(zapcore.DebugLevel) case logger.Silent: New(zapcore.ErrorLevel) - case "debug" == logger.LogLevel: + case logger.LogLevel == "debug": New(zapcore.DebugLevel) - case "error" == logger.LogLevel: + case logger.LogLevel == "error": New(zapcore.ErrorLevel) default: New(zapcore.InfoLevel) @@ -45,23 +45,19 @@ func New(level zapcore.Level) { config.Level = logLevel encoderConfig := zapcore.EncoderConfig{ - TimeKey: "ts", + TimeKey: "timestamp", LevelKey: "level", NameKey: "logger", FunctionKey: zapcore.OmitKey, MessageKey: "msg", - StacktraceKey: "stacktrace", + StacktraceKey: "", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.EpochTimeEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } - encoderConfig.TimeKey = "timestamp" - encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - encoderConfig.StacktraceKey = "" - config.EncoderConfig = encoderConfig config.Encoding = "console" config.OutputPaths = []string{"stdout"} @@ -75,10 +71,18 @@ func New(level zapcore.Level) { } } +func shouldPrint() bool { + core := log.Core() + errorEnabled := core.Enabled(zapcore.ErrorLevel) + debugEnabled := core.Enabled(zapcore.DebugLevel) + infoEnabled := core.Enabled(zapcore.InfoLevel) + + return !(errorEnabled && !debugEnabled && !infoEnabled) +} + // FInfo I need to check if the debug is false because the error comes in the debug also as true func FInfo(w io.Writer, message string) { - if !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.DebugLevel)) || - !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.InfoLevel)) { + if shouldPrint() { fmt.Fprintf(w, "%s", message) // nolint:all } } @@ -88,22 +92,19 @@ func FInfoFlags(w io.Writer, message, format, out string) { return } - if !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.DebugLevel)) || - !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.InfoLevel)) { + if shouldPrint() { fmt.Fprintf(w, "%s", message) // nolint:all } } func PrintHeader(table tablecli.Table, format string) { - if !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.DebugLevel)) || - !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.InfoLevel)) { + if shouldPrint() { table.PrintHeader(format) } } func PrintRow(table tablecli.Table, format string, row []string) { - if !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.DebugLevel)) || - !(log.Core().Enabled(zapcore.ErrorLevel) && !log.Core().Enabled(zapcore.InfoLevel)) { + if shouldPrint() { table.PrintRow(format, row) } } diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index 5d42c88e..6896ec17 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -168,6 +168,9 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication return err } + // Cache string conversion of application ID (used multiple times in loop) + appIDStr := strconv.FormatInt(conf.Application.ID, 10) + for _, funcMan := range manifest.Applications[0].FunctionsInstances { if funcConf := FunctionIds[funcMan.Function]; funcConf.InstanceID > 0 { request := apiApplications.UpdateInstanceRequest{} @@ -176,8 +179,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication request.SetArgs(funcMan.Args) request.SetName(funcMan.Name) idString := strconv.FormatInt(funcConf.InstanceID, 10) - appID := strconv.FormatInt(conf.Application.ID, 10) - _, err := client.UpdateInstance(ctx, &request, appID, idString) + _, err := client.UpdateInstance(ctx, &request, appIDStr, idString) if err != nil { return err } @@ -187,8 +189,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication request.SetArgs(funcMan.Args) request.SetName(funcMan.Name) request.SetFunction(funcConf.ID) - appId := strconv.FormatInt(conf.Application.ID, 10) - resp, err := client.CreateFuncInstances(ctx, &request, appId) + resp, err := client.CreateFuncInstances(ctx, &request, appIDStr) if err != nil { return err } @@ -296,7 +297,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication conn.Name = storage.GetName() // storage does not contain addresses default: - return errors.New("Failed to get Connector type") + return errors.New("failed to get Connector type") } connectorConf = append(connectorConf, conn) } else { @@ -322,7 +323,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication conn.Id = storage.GetId() conn.Name = storage.GetName() default: - return errors.New("Failed to get Connector type") + return errors.New("failed to get Connector type") } ConnectorIds[conn.Name] = conn.Id connectorConf = append(connectorConf, conn) @@ -338,13 +339,16 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication ruleConf := []contracts.AzionJsonDataRules{} if len(edgeappman.Rules) > 0 { + // Cache string conversion of application ID (used multiple times in rules loop) + appIDStr := strconv.FormatInt(conf.Application.ID, 10) + for _, rule := range edgeappman.Rules { if r := RuleIds[rule.Rule.Name]; r.Id > 0 { switch rule.Phase { case "request": req := transformRuleRequest(rule.Rule) strid := strconv.FormatInt(r.Id, 10) - req.IdApplication = strconv.FormatInt(conf.Application.ID, 10) + req.IdApplication = appIDStr req.Id = strid behs, err := transformBehaviorsRequest(rule.Rule.Behaviors) if errors.Is(err, utils.ErrorNotFound404) { @@ -371,7 +375,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication case "response": req := transformRuleResponse(rule.Rule) strid := strconv.FormatInt(r.Id, 10) - req.IdApplication = strconv.FormatInt(conf.Application.ID, 10) + req.IdApplication = appIDStr req.Id = strid behs, err := transformBehaviorsResponse(rule.Rule.Behaviors) if errors.Is(err, utils.ErrorNotFound404) { @@ -410,8 +414,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication } req.ApplicationRequestPhaseRuleEngineRequest = createRequest req.Behaviors = bh - appstring := strconv.FormatInt(conf.Application.ID, 10) - created, err := client.CreateRulesEngineRequest(ctx, appstring, rule.Phase, req) + created, err := client.CreateRulesEngineRequest(ctx, appIDStr, rule.Phase, req) if err != nil { return err } @@ -430,8 +433,7 @@ func (man *ManifestInterpreter) CreateResources(conf *contracts.AzionApplication } req.ApplicationResponsePhaseRuleEngineRequest = createRequest req.Behaviors = bh - appstring := strconv.FormatInt(conf.Application.ID, 10) - created, err := client.CreateRulesEngineResponse(ctx, appstring, rule.Phase, req) + created, err := client.CreateRulesEngineResponse(ctx, appIDStr, rule.Phase, req) if err != nil { return err } @@ -549,6 +551,9 @@ func deleteResources(ctx context.Context, f *cmdutil.Factory, conf *contracts.Az return nil } + // Cache string conversion of application ID (used in delete loop) + appIDStr := strconv.FormatInt(conf.Application.ID, 10) + for _, value := range RuleIds { //since until [UXE-3599] was carried out we'd only cared about "request" phase, this check guarantees that if Phase is empty // we are probably dealing with a rule engine from a previous version @@ -556,15 +561,14 @@ func deleteResources(ctx context.Context, f *cmdutil.Factory, conf *contracts.Az if value.Phase != "" { phase = value.Phase } - str := strconv.FormatInt(conf.Application.ID, 10) strRule := strconv.FormatInt(value.Id, 10) var statusInt int var err error switch phase { case "request": - statusInt, err = client.DeleteRulesEngineRequest(ctx, str, phase, strRule) + statusInt, err = client.DeleteRulesEngineRequest(ctx, appIDStr, phase, strRule) case "response": - statusInt, err = client.DeleteRulesEngineResponse(ctx, str, phase, strRule) + statusInt, err = client.DeleteRulesEngineResponse(ctx, appIDStr, phase, strRule) default: return msgrule.ErrorInvalidPhase } diff --git a/pkg/manifest/request.go b/pkg/manifest/request.go index 39aa2ad9..b44f8973 100644 --- a/pkg/manifest/request.go +++ b/pkg/manifest/request.go @@ -19,9 +19,8 @@ import ( ) func transformEdgeConnectorRequest(connectorRequest edgesdk.ConnectorPolymorphicRequest) *apiConnector.UpdateRequest { - request := &apiConnector.UpdateRequest{} - if connectorRequest.ConnectorHTTPRequest != nil { + request := &apiConnector.UpdateRequest{} bodyRequest := connectorRequest.ConnectorHTTPRequest atts := bodyRequest.Attributes body := edgesdk.PatchedConnectorHTTPRequest{} @@ -41,12 +40,12 @@ func transformEdgeConnectorRequest(connectorRequest edgesdk.ConnectorPolymorphic } if connectorRequest.ConnectorLiveIngestRequest != nil { + request := &apiConnector.UpdateRequest{} body := edgesdk.PatchedConnectorLiveIngestRequest{} bodyRequest := connectorRequest.ConnectorLiveIngestRequest if bodyRequest.Active != nil { body.SetActive(*bodyRequest.Active) } - body.SetType(bodyRequest.Type) body.SetAttributes(bodyRequest.Attributes) if bodyRequest.Name != "" { @@ -61,8 +60,9 @@ func transformEdgeConnectorRequest(connectorRequest edgesdk.ConnectorPolymorphic } if connectorRequest.ConnectorStorageRequest != nil { - body := edgesdk.PatchedConnectorStorageRequest{} + request := &apiConnector.UpdateRequest{} bodyRequest := connectorRequest.ConnectorStorageRequest + body := edgesdk.PatchedConnectorStorageRequest{} if bodyRequest.Active != nil { body.SetActive(*bodyRequest.Active) } @@ -79,7 +79,7 @@ func transformEdgeConnectorRequest(connectorRequest edgesdk.ConnectorPolymorphic return request } - return request + return &apiConnector.UpdateRequest{} } func transformWorkloadRequestUpdate(createRequest contracts.WorkloadManifest) *apiWorkloads.UpdateRequest { @@ -190,13 +190,6 @@ func transformEdgeApplicationRequestUpdate(edgeapprequest contracts.Applications if edgeapprequest.Debug != nil { request.SetDebug(*edgeapprequest.Debug) } - type Modules struct { - EdgeCacheEnabled bool `json:"edge_cache_enabled"` - EdgeFunctionsEnabled bool `json:"edge_functions_enabled"` - ApplicationAcceleratorEnabled bool `json:"application_accelerator_enabled"` - ImageProcessorEnabled bool `json:"image_processor_enabled"` - TieredCacheEnabled bool `json:"tiered_cache_enabled"` - } if edgeapprequest.Modules != nil { request.SetModules(*edgeapprequest.Modules) } @@ -217,33 +210,9 @@ func transformEdgeApplicationRequestCreate(edgeapprequest contracts.Applications if edgeapprequest.Debug != nil { request.SetDebug(*edgeapprequest.Debug) } - type Modules struct { - EdgeCacheEnabled bool `json:"edge_cache_enabled"` - EdgeFunctionsEnabled bool `json:"edge_functions_enabled"` - ApplicationAcceleratorEnabled bool `json:"application_accelerator_enabled"` - ImageProcessorEnabled bool `json:"image_processor_enabled"` - TieredCacheEnabled bool `json:"tiered_cache_enabled"` - } - // if edgeapprequest.Modules != nil { - // modules := edgesdk.EdgeApplicationModulesRequest{} - // if edgeapprequest.Modules.ApplicationAcceleratorEnabled != nil { - // modules.SetApplicationAcceleratorEnabled(*edgeapprequest.Modules.ApplicationAcceleratorEnabled) - // } - // if edgeapprequest.Modules.EdgeCacheEnabled != nil { - // modules.SetEdgeCacheEnabled(*edgeapprequest.Modules.EdgeCacheEnabled) - // } - // if edgeapprequest.Modules.EdgeFunctionsEnabled != nil { - // modules.SetEdgeFunctionsEnabled(*edgeapprequest.Modules.EdgeFunctionsEnabled) - // } - // if edgeapprequest.Modules.ImageProcessorEnabled != nil { - // modules.SetImageProcessorEnabled(*edgeapprequest.Modules.ImageProcessorEnabled) - // } - // if edgeapprequest.Modules.TieredCacheEnabled != nil { - // modules.SetTieredCacheEnabled(*edgeapprequest.Modules.TieredCacheEnabled) - // } - - // request.SetModules(modules) - // } + if edgeapprequest.Modules != nil { + request.SetModules(*edgeapprequest.Modules) + } if edgeapprequest.Name != "" { request.SetName(edgeapprequest.Name) @@ -290,9 +259,6 @@ func transformRuleResponse(rule contracts.ManifestRule) *apiApplications.UpdateR request := &apiApplications.UpdateRulesEngineResponse{} request.SetActive(rule.Active) - // if rule.Behaviors != nil { - // request.SetBehaviors(rule.Behaviors) - // } if rule.Criteria != nil { request.SetCriteria(rule.Criteria) } @@ -334,7 +300,7 @@ func getConnectorName(connector edgesdk.ConnectorPolymorphicRequest, defaultName } func transformBehaviorsRequest(behaviors []contracts.ManifestRuleBehavior) ([]edgesdk.ApplicationRuleEngineRequestPhaseBehaviorsRequest, error) { - behaviorsRequest := []edgesdk.ApplicationRuleEngineRequestPhaseBehaviorsRequest{} + behaviorsRequest := make([]edgesdk.ApplicationRuleEngineRequestPhaseBehaviorsRequest, 0, len(behaviors)) for _, behavior := range behaviors { var withArgs edgesdk.ApplicationRequestPhaseBehaviorWithArgsRequest var withoutArgs edgesdk.ApplicationRequestPhaseBehaviorWithoutArgsRequest @@ -460,7 +426,7 @@ func transformBehaviorsRequest(behaviors []contracts.ManifestRuleBehavior) ([]ed } func transformBehaviorsResponse(behaviors []contracts.ManifestRuleBehavior) ([]edgesdk.ApplicationRuleEngineResponsePhaseBehaviorsRequest, error) { - behaviorsResponse := []edgesdk.ApplicationRuleEngineResponsePhaseBehaviorsRequest{} + behaviorsResponse := make([]edgesdk.ApplicationRuleEngineResponsePhaseBehaviorsRequest, 0, len(behaviors)) for _, behavior := range behaviors { var withArgs edgesdk.ApplicationResponsePhaseBehaviorWithArgsRequest diff --git a/pkg/metric/count.go b/pkg/metric/count.go index c28a5b32..d530d421 100644 --- a/pkg/metric/count.go +++ b/pkg/metric/count.go @@ -3,7 +3,6 @@ package metric import ( "encoding/json" "io" - "log" "os" "path/filepath" @@ -24,6 +23,11 @@ type command struct { Shell string } +var ignoredCommands = map[string]bool{ + "__complete": true, + "completion": true, +} + func TotalCommandsCount(cmd cmdutil.Command, commandName string, executionTime float64, errExec error, profile string) error { if commandName == "" { return nil @@ -38,12 +42,8 @@ func TotalCommandsCount(cmd cmdutil.Command, commandName string, executionTime f if profile != "" { dir.Dir = filepath.Join(dir.Dir, profile) } - ignoredWords := map[string]bool{ - "__complete": true, - "completion": true, - } - if ignoredWords[commandName] { + if ignoredCommands[commandName] { return nil } @@ -110,7 +110,7 @@ func TotalCommandsCount(cmd cmdutil.Command, commandName string, executionTime f // Encode and write the updated map back to the file if err := json.NewEncoder(file).Encode(data); err != nil { - log.Fatal(err) + return err } return nil diff --git a/pkg/output/describe.go b/pkg/output/describe.go index d1d8c46e..a95d2b5a 100644 --- a/pkg/output/describe.go +++ b/pkg/output/describe.go @@ -18,15 +18,15 @@ type DescribeOutput struct { } func (d *DescribeOutput) Format() (bool, error) { - formated := false + formatted := false if len(d.Flags.Format) > 0 || len(d.Flags.Out) > 0 { - formated = true + formatted = true err := format(d.Values, d.GeneralOutput) if err != nil { - return formated, err + return formatted, err } } - return formated, nil + return formatted, nil } func (c *DescribeOutput) Output() { @@ -75,15 +75,19 @@ func (c *DescribeOutput) Output() { func checkPrimitiveType(value any) any { valueType := reflect.TypeOf(value) - if valueType != nil && (valueType.Kind() == reflect.Int || valueType.Kind() == reflect.String || - valueType.Kind() == reflect.Bool || valueType.Kind() == reflect.Float32 || - valueType.Kind() == reflect.Float64 || valueType.Kind() == reflect.Uint || - valueType.Kind() == reflect.Uint8 || valueType.Kind() == reflect.Uint16 || - valueType.Kind() == reflect.Uint32 || valueType.Kind() == reflect.Uint64 || - valueType.Kind() == reflect.Int8 || valueType.Kind() == reflect.Int16 || - valueType.Kind() == reflect.Int32 || valueType.Kind() == reflect.Int64) { + if valueType == nil { + jsonValue, _ := json.Marshal(value) + return string(jsonValue) + } + + switch valueType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, + reflect.Bool, reflect.String: return value + default: + jsonValue, _ := json.Marshal(value) + return string(jsonValue) } - jsonValue, _ := json.Marshal(value) - return string(jsonValue) } diff --git a/pkg/output/format.go b/pkg/output/format.go index 071be804..3cf3fa6b 100644 --- a/pkg/output/format.go +++ b/pkg/output/format.go @@ -25,11 +25,6 @@ func format(v any, g GeneralOutput) error { var err error switch g.Flags.Format { - case JSON: - b, err = json.MarshalIndent(v, "", " ") - if err != nil { - return err - } case YAML, YML: b, err = yaml.Marshal(v) if err != nil { @@ -40,6 +35,8 @@ func format(v any, g GeneralOutput) error { if err != nil { return err } + case JSON: + fallthrough default: b, err = json.MarshalIndent(v, "", " ") if err != nil { diff --git a/pkg/output/general.go b/pkg/output/general.go index baefed6b..f72c7576 100644 --- a/pkg/output/general.go +++ b/pkg/output/general.go @@ -16,15 +16,15 @@ type GeneralOutput struct { } func (g *GeneralOutput) Format() (bool, error) { - formated := false + formatted := false if len(g.Flags.Format) > 0 || len(g.Flags.Out) > 0 { - formated = true + formatted = true err := format(g, *g) if err != nil { - return formated, err + return formatted, err } } - return formated, nil + return formatted, nil } func (g *GeneralOutput) Output() { diff --git a/pkg/output/list.go b/pkg/output/list.go index c98d29a6..88f70ff4 100644 --- a/pkg/output/list.go +++ b/pkg/output/list.go @@ -15,15 +15,15 @@ type ListOutput struct { } func (l *ListOutput) Format() (bool, error) { - formated := false + formatted := false if len(l.Flags.Format) > 0 || len(l.Flags.Out) > 0 { - formated = true + formatted = true err := format(l, l.GeneralOutput) if err != nil { - return formated, err + return formatted, err } } - return formated, nil + return formatted, nil } func (c *ListOutput) Output() { diff --git a/pkg/output/slice.go b/pkg/output/slice.go index 49ef1e73..5bf6bc8b 100644 --- a/pkg/output/slice.go +++ b/pkg/output/slice.go @@ -6,12 +6,12 @@ type SliceOutput struct { } func (i *SliceOutput) Format() (bool, error) { - formated := false + formatted := false if len(i.Flags.Format) > 0 || len(i.Flags.Out) > 0 { - formated = true + formatted = true err := format(i, i.GeneralOutput) if err != nil { - return formated, err + return formatted, err } } return true, nil diff --git a/pkg/schedule/schedule.go b/pkg/schedule/schedule.go index ac93e54e..a0e358dc 100644 --- a/pkg/schedule/schedule.go +++ b/pkg/schedule/schedule.go @@ -58,13 +58,18 @@ type Schedule struct { Kind string `json:"kind"` } -func NewSchedule(fact *Factory, name string, kind string) error { +func NewSchedule(fact *Factory, cmdFactory *cmdutil.Factory, name string, kind string) error { factory := InjectFactory(fact) factory.Schedule.Name = name factory.Schedule.Time = time.Now() factory.Schedule.Kind = kind - schedules, err := factory.readFileSchedule() + var activeProfile string + if cmdFactory != nil { + activeProfile = cmdFactory.GetActiveProfile() + } + + schedules, err := factory.readFileScheduleForProfile(activeProfile) if err != nil { logger.Debug("Error while reading the schedule", zap.Error(err)) return err @@ -72,7 +77,7 @@ func NewSchedule(fact *Factory, name string, kind string) error { schedules = append(schedules, factory.Schedule) - err = factory.createFileSchedule(schedules) + err = factory.createFileScheduleForProfile(schedules, activeProfile) if err != nil { logger.Debug("Scheduling error", zap.Error(err)) return err @@ -80,16 +85,6 @@ func NewSchedule(fact *Factory, name string, kind string) error { return nil } -func (s *Factory) createFileSchedule(shedules []Schedule) error { - b, err := s.MarshalIndent(shedules, " ", " ") - if err != nil { - return err - } - configPath := s.Dir() - path := s.Join(configPath.Dir, configPath.Schedule) - return s.WriteFile(path, b, os.FileMode(os.O_CREATE)) -} - func (s *Factory) createFileScheduleForProfile(schedules []Schedule, profile string) error { b, err := s.MarshalIndent(schedules, " ", " ") if err != nil { @@ -103,38 +98,6 @@ func (s *Factory) createFileScheduleForProfile(schedules []Schedule, profile str return s.WriteFile(path, b, 0666) } -func (s *Factory) readFileSchedule() ([]Schedule, error) { - configPath := config.Dir() - schedules := []Schedule{} - - path := s.Join(configPath.Dir, configPath.Schedule) - - // Checks if the file exists in the given path - if _, err := s.Stat(path); s.IsNotExist(err) { - data, err := s.Marshal(&schedules) - if err != nil { - return nil, err - } - err = s.WriteFile(path, data, 0666) - if err != nil { - return nil, err - } - - return schedules, nil - } - - file, err := s.ReadFile(path) - if err != nil { - return nil, err - } - - err = s.Unmarshal(file, &schedules) - if err != nil { - return nil, err - } - return schedules, nil -} - func (s *Factory) readFileScheduleForProfile(profile string) ([]Schedule, error) { configPath := config.Dir() if profile != "" { diff --git a/pkg/schedule/schedule_test.go b/pkg/schedule/schedule_test.go index dd2e14bf..b16e61c8 100644 --- a/pkg/schedule/schedule_test.go +++ b/pkg/schedule/schedule_test.go @@ -107,7 +107,7 @@ func TestNewSchedule(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := NewSchedule(&tt.factory, tt.args.name, tt.args.kind); (err != nil) != tt.wantErr { + if err := NewSchedule(&tt.factory, nil, tt.args.name, tt.args.kind); (err != nil) != tt.wantErr { t.Errorf("NewSchedule() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/pkg/v3commands/create/domain/domain.go b/pkg/v3commands/create/domain/domain.go index 194afa99..6a06519b 100644 --- a/pkg/v3commands/create/domain/domain.go +++ b/pkg/v3commands/create/domain/domain.go @@ -22,7 +22,7 @@ type Fields struct { Name string `json:"name"` Cnames []string `json:"cnames"` CnameAccessOnly string `json:"cname_access_only"` - EdgeApplicationID int `json:"edge_application_id"` + EdgeApplicationID int64 `json:"edge_application_id"` DigitalCertificateID string `json:"digital_certificate_id"` IsActive string `json:"is_active"` Path string @@ -63,7 +63,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { return msg.ErrorConvertApplicationID } - fields.EdgeApplicationID = int(num) + fields.EdgeApplicationID = num } if !cmd.Flags().Changed("name") { @@ -89,7 +89,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { request.SetName(fields.Name) request.SetCnames(fields.Cnames) - request.SetEdgeApplicationId(int64(fields.EdgeApplicationID)) + request.SetEdgeApplicationId(fields.EdgeApplicationID) if cmd.Flags().Changed("digital-certificate-id") { request.SetDigitalCertificateId(fields.DigitalCertificateID) @@ -122,7 +122,7 @@ func NewCmd(f *cmdutil.Factory) *cobra.Command { flags.StringSliceVar(&fields.Cnames, "cnames", []string{}, msg.FlagCnames) flags.StringVar(&fields.CnameAccessOnly, "cname-access-only", "false", msg.FlagCnameAccessOnly) flags.StringVar(&fields.DigitalCertificateID, "digital-certificate-id", "", msg.FlagDigitalCertificateID) - flags.IntVar(&fields.EdgeApplicationID, "application-id", 0, msg.FlagEdgeApplicationId) + flags.Int64Var(&fields.EdgeApplicationID, "application-id", 0, msg.FlagEdgeApplicationId) flags.StringVar(&fields.IsActive, "active", "true", msg.FlagIsActive) flags.StringVar(&fields.Path, "file", "", msg.FlagFile) flags.BoolP("help", "h", false, msg.HelpFlag) diff --git a/pkg/v3commands/delete/edge_storage/bucket/bucket.go b/pkg/v3commands/delete/edge_storage/bucket/bucket.go index 86f1c628..fb0dc5b5 100644 --- a/pkg/v3commands/delete/edge_storage/bucket/bucket.go +++ b/pkg/v3commands/delete/edge_storage/bucket/bucket.go @@ -92,7 +92,7 @@ func NewBucketCmd(delete *DeleteBucketCmd, f *cmdutil.Factory) *cobra.Command { if err != nil { if strings.Contains(err.Error(), msg.ERROR_NO_EMPTY_BUCKET) { logger.FInfo(f.IOStreams.Out, "Bucket deletion was scheduled successfully\n") - return schedule.NewSchedule(nil, bucketName, schedule.DELETE_BUCKET) + return schedule.NewSchedule(nil, f, bucketName, schedule.DELETE_BUCKET) } else { return fmt.Errorf(msg.ERROR_DELETE_BUCKET, err.Error()) } diff --git a/pkg/v3commands/dev/dev.go b/pkg/v3commands/dev/dev.go index 71411614..a3a99238 100644 --- a/pkg/v3commands/dev/dev.go +++ b/pkg/v3commands/dev/dev.go @@ -16,8 +16,9 @@ import ( ) var ( - isFirewall bool - port int + isFirewall bool + port int + SkipFramework bool ) type DevCmd struct { @@ -59,6 +60,7 @@ func NewCobraCmd(dev *DevCmd) *cobra.Command { devCmd.Flags().BoolP("help", "h", false, msg.DevFlagHelp) devCmd.Flags().IntVar(&port, "port", 0, msg.PortFlag) devCmd.Flags().BoolVar(&isFirewall, "firewall", false, msg.IsFirewall) + devCmd.Flags().BoolVar(&SkipFramework, "skip-framework-build", false, msg.SkipFrameworkBuild) return devCmd } diff --git a/pkg/v3commands/dev/vulcan.go b/pkg/v3commands/dev/vulcan.go index 73a177d0..f36e1119 100644 --- a/pkg/v3commands/dev/vulcan.go +++ b/pkg/v3commands/dev/vulcan.go @@ -21,6 +21,10 @@ func vulcan(cmd *DevCmd, isFirewall bool, port int) error { command = fmt.Sprintf("%s --firewall", command) } + if SkipFramework { + command = fmt.Sprintf("%s --skip-framework-build", command) + } + err := runCommand(cmd, command) if err != nil { return fmt.Errorf(msg.ErrorVulcanExecute.Error(), err.Error()) diff --git a/pkg/v3commands/init/init_test.go b/pkg/v3commands/init/init_test.go index 7b74fa08..6571f67d 100644 --- a/pkg/v3commands/init/init_test.go +++ b/pkg/v3commands/init/init_test.go @@ -34,8 +34,6 @@ func TestNewCmd(t *testing.T) { NewCmd(f) } -var cloneOptions git.CloneOptions - func Test_initCmd_Run(t *testing.T) { logger.New(zapcore.DebugLevel) diff --git a/pkg/vulcan/vulcan.go b/pkg/vulcan/vulcan.go index d3a18186..0bebcdce 100644 --- a/pkg/vulcan/vulcan.go +++ b/pkg/vulcan/vulcan.go @@ -14,8 +14,8 @@ import ( var ( currentMajor = 6 installEdgeFunctions = "npx --yes %s edge-functions%s %s" - firstTimeExecuting = "@6.2.0" - versionVulcan = "@6.2.0" + firstTimeExecuting = "@6.2.1" + versionVulcan = "@6.2.1" ) type VulcanPkg struct { @@ -33,9 +33,9 @@ func NewVulcan() *VulcanPkg { } func NewVulcanV3() *VulcanPkg { - versionVulcan = "@5.3.0" + versionVulcan = "@5.3.1" currentMajor = 5 - firstTimeExecuting = "@5.3.0" + firstTimeExecuting = "@5.3.1" return &VulcanPkg{ Command: command, CheckVulcanMajor: checkVulcanMajor, diff --git a/pkg/vulcan/vulcan_test.go b/pkg/vulcan/vulcan_test.go index 92d3708c..cc502579 100644 --- a/pkg/vulcan/vulcan_test.go +++ b/pkg/vulcan/vulcan_test.go @@ -105,7 +105,7 @@ func TestCheckVulcanMajor(t *testing.T) { { name: "new major version without last version", args: args{ - currentVersion: "6.2.0", + currentVersion: "6.2.1", }, lastVulcanVer: "", expectedVersion: firstTimeExecuting, @@ -116,8 +116,8 @@ func TestCheckVulcanMajor(t *testing.T) { args: args{ currentVersion: "7.0.2", }, - lastVulcanVer: "6.2.0", - expectedVersion: "@6.2.0", + lastVulcanVer: "6.2.1", + expectedVersion: "@6.2.1", wantErr: false, }, { @@ -125,8 +125,8 @@ func TestCheckVulcanMajor(t *testing.T) { args: args{ currentVersion: "7.0.2", }, - lastVulcanVer: "6.2.0", - expectedVersion: "@6.2.0", + lastVulcanVer: "6.2.1", + expectedVersion: "@6.2.1", wantErr: false, }, { diff --git a/utils/helpers.go b/utils/helpers.go index 1761213b..79a6a382 100644 --- a/utils/helpers.go +++ b/utils/helpers.go @@ -571,10 +571,8 @@ func checkStatusCode400Error(httpResp *http.Response) error { return err } - result := strings.ReplaceAll(string(responseBody), "{", "") - result = strings.ReplaceAll(result, "}", "") - result = strings.ReplaceAll(result, "[", "") - result = strings.ReplaceAll(result, "]", "") + replacer := strings.NewReplacer("{", "", "}", "", "[", "", "]", "") + result := replacer.Replace(string(responseBody)) return fmt.Errorf("%s", result) } @@ -850,8 +848,8 @@ func Select(prompter Prompter) (string, error) { func Concat(strs ...string) string { var sb strings.Builder - for i := 0; i < len(strs); i++ { - sb.WriteString(strs[i]) + for _, str := range strs { + sb.WriteString(str) } return sb.String() }