├── .devcontainer ├── Dockerfile ├── DockerfileDocs ├── devcontainer.json ├── docker-compose.yml └── files │ ├── devops-welcome.sh │ └── devops-zshrc-specific ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── boring-cyborg.yml ├── dependabot.yml ├── issue_label_bot.yaml ├── pull_request_template.md ├── release-drafter.yml ├── settings.yml ├── stale.yml └── workflows │ ├── cicd-build.yml │ ├── cicd-docs.yml │ ├── cicd-release.yml │ ├── devcontainers.yml │ ├── gorelease.yaml │ ├── release-drafter.yml │ └── shellcheck.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .spelling ├── LICENSE ├── Makefile ├── README.md ├── client └── client.go ├── docs ├── data-sources │ ├── harbor_label.md │ ├── harbor_project.md │ └── harbor_registry.md └── resources │ ├── harbor_config_system.md │ ├── harbor_configuration.md │ ├── harbor_label.md │ ├── harbor_project.md │ ├── harbor_project_member.md │ ├── harbor_registry.md │ ├── harbor_replication.md │ ├── harbor_retention_policy.md │ ├── harbor_robot_account.md │ ├── harbor_tasks.md │ └── harbor_usergroup.md ├── documentation ├── adr │ ├── 0000-use-markdown-architectural-decision-records.md │ ├── 0001-use-swagger-for-generate-http-client.md │ ├── index.md │ └── template.md ├── go.mod ├── go.sum ├── mage.go ├── magefile.go ├── mkdocs │ ├── css │ │ ├── extras.css │ │ └── table.css │ ├── guides │ │ ├── development.md │ │ └── kind.md │ ├── index.md │ └── js │ │ └── table.js └── provider_doc │ ├── data-sources │ ├── harbor_label.mdpp │ ├── harbor_project.mdpp │ └── harbor_registry.mdpp │ └── resources │ ├── harbor_config_system.mdpp │ ├── harbor_configuration.mdpp │ ├── harbor_label.mdpp │ ├── harbor_project.mdpp │ ├── harbor_project_member.mdpp │ ├── harbor_registry.mdpp │ ├── harbor_replication.mdpp │ ├── harbor_retention_policy.mdpp │ ├── harbor_robot_account.mdpp │ ├── harbor_tasks.mdpp │ └── harbor_usergroup.mdpp ├── examples ├── test │ └── terraform_harbor_integration_test.go ├── tf-acception-test-part-2 │ ├── data_label.tf │ ├── data_project.tf │ ├── data_registry.tf │ ├── label.tf │ └── provider.tf ├── tf-acception-test │ ├── label.tf │ ├── outputs.tf │ ├── project.tf │ ├── project_member.tf │ ├── provider.tf │ ├── registry.tf │ ├── replication.tf │ ├── retention_policy.tf │ ├── robot_account.tf │ ├── tasks.tf │ └── usergroup.tf └── tf-project-only │ ├── main.tf │ └── provider.tf ├── gen └── .keep ├── go.mod ├── go.sum ├── harbor ├── data_source_label.go ├── data_source_label_test.go ├── data_source_project.go ├── data_source_project_test.go ├── data_source_registry.go ├── data_source_registry_test.go ├── provider.go ├── provider_test.go ├── resource_config_auth.go ├── resource_config_email.go ├── resource_config_system.go ├── resource_labels.go ├── resource_labels_test.go ├── resource_project.go ├── resource_project_member.go ├── resource_project_member_test.go ├── resource_project_test.go ├── resource_registry.go ├── resource_registry_test.go ├── resource_replication.go ├── resource_replication_test.go ├── resource_retention_policy.go ├── resource_retention_policy_test.go ├── resource_robot_account.go ├── resource_robot_account_test.go ├── resource_tasks.go ├── resource_usergroup.go ├── resource_usergroup_test.go ├── schema_retention_policy.go └── structure_retention_policy.go ├── main.go ├── mkdocs.yml ├── scripts ├── build-04-go-errorchecks.sh ├── swagger-specs │ ├── patch.1.json │ └── v2-swagger-original.json ├── tst-15-execute-classic-acc.sh └── tst-15-execute-go-acc.sh └── tools ├── go.mod ├── go.sum ├── mage.go └── magefile.go /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.pkg.github.com/nolte/vscode-devcontainers/k8s-operator:latest 2 | 3 | # https://github.com/bflad/tfproviderdocs/releases 4 | ENV TFPROVIDERDOCS_VERSION=0.7.0 5 | 6 | # https://github.com/bats-core/bats-core 7 | ENV BATS_VERSION=1.2.0 8 | 9 | USER root 10 | 11 | COPY --from=docker.pkg.github.com/nolte/vscode-devcontainers/devops:latest /usr/local/bin/swagger /usr/local/bin/swagger 12 | COPY --from=docker.pkg.github.com/nolte/vscode-devcontainers/devops:latest /usr/local/bin/terraform /usr/local/bin/terraform_012 13 | COPY --from=docker.pkg.github.com/nolte/vscode-devcontainers/devops:latest /usr/local/bin/terraform-doc /usr/local/bin/terraform-doc 14 | COPY --from=docker.pkg.github.com/nolte/vscode-devcontainers/devops:latest /usr/local/bin/shellcheck /usr/local/bin/shellcheck 15 | 16 | # https://github.com/hashicorp/terraform/releases 17 | ENV TERRAFORM_VERSION=0.13.4 18 | 19 | RUN curl -Lo ./terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ 20 | && unzip ./terraform.zip \ 21 | && mv ./terraform /usr/local/bin/terraform \ 22 | && chmod +x /usr/local/bin/terraform \ 23 | && rm ./terraform.zip 24 | 25 | 26 | 27 | RUN curl -sSL -k https://github.com/bflad/tfproviderdocs/releases/download/v${TFPROVIDERDOCS_VERSION}/tfproviderdocs_${TFPROVIDERDOCS_VERSION}_linux_amd64.tar.gz -o /tmp/tfproviderdocs.tgz \ 28 | && tar -zxf /tmp/tfproviderdocs.tgz -C /tmp \ 29 | && mv /tmp/tfproviderdocs /usr/local/bin/ 30 | 31 | RUN apk add --update-cache \ 32 | nodejs npm \ 33 | && rm -rf /var/cache/apk/* 34 | 35 | RUN mkdir -p /go/src && chown -R ${USER_UID}:${USER_GID} /go/src \ 36 | && mkdir -p /go/pkg && chown -R ${USER_UID}:${USER_GID} /go/pkg 37 | 38 | RUN curl -sSL -k https://github.com/goreleaser/goreleaser/releases/download/v0.141.0/goreleaser_Linux_x86_64.tar.gz -o /tmp/goreleaser.tgz \ 39 | && tar -zxf /tmp/goreleaser.tgz -C /tmp \ 40 | && mv /tmp/goreleaser /usr/local/bin/ 41 | 42 | 43 | USER ${USERNAME} 44 | 45 | #COPY --chown=${USER_UID}:${USER_GID} --from=docker.pkg.github.com/nolte/vscode-devcontainers/devops:latest /home/${USERNAME}/.zshrc-specific /home/${USERNAME}/.zshrc-specific 46 | COPY --chown=${USER_UID}:${USER_GID} files/devops-zshrc-specific /home/${USERNAME}/.zshrc-specific 47 | COPY --chown=${USER_UID}:${USER_GID} files/devops-welcome.sh /home/${USERNAME}/.welcome.sh 48 | 49 | RUN mkdir -p /home/${USERNAME}/.terraform.d/plugins/linux_amd64 50 | 51 | ENV PATH="/usr/local/share/npm/bin:/usr/local/bin:/usr/local/sbin:~/bin:$PATH" 52 | 53 | RUN mkdir "/home/${USERNAME}/.npm-packages" 54 | RUN npm config set prefix "/home/${USERNAME}/.npm-packages" 55 | RUN npm install swagger-merger --user -g 56 | 57 | RUN helm repo add harbor https://helm.goharbor.io 58 | RUN npm install swagger-merger -g 59 | 60 | RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 61 | 62 | RUN curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.3.0 63 | 64 | RUN npm i markdown-spellcheck -g 65 | 66 | RUN curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash 67 | 68 | RUN npm i madr adr-log -g 69 | 70 | RUN mkdir -p ${WORKON_HOME} \ 71 | && virtualenv -p python3 ${WORKON_HOME}/development \ 72 | && source ${WORKON_HOME}/development/bin/activate \ 73 | && pip install MarkdownPP mkdocs \ 74 | && deactivate 75 | -------------------------------------------------------------------------------- /.devcontainer/DockerfileDocs: -------------------------------------------------------------------------------- 1 | FROM python:alpine 2 | 3 | RUN echo "**** install Python ****" && \ 4 | apk add --update-cache \ 5 | linux-headers gnupg openssl gcc build-base libc-dev musl-dev ca-certificates \ 6 | && rm -rf /var/cache/apk/* 7 | 8 | ARG USERNAME=vscode 9 | ARG USER_UID=1000 10 | ARG USER_GID=1000 11 | 12 | RUN adduser $USERNAME -s /bin/sh -D -u $USER_UID $USER_GID 13 | 14 | RUN pip install mkdocs pymdown-extensions Pygments 15 | 16 | RUN mkdir -p /workspace \ 17 | && chown -R ${USER_UID}:${USER_UID} /workspace 18 | 19 | USER ${USERNAME} 20 | 21 | WORKDIR /workspace 22 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terraform-provider-harbor", 3 | "dockerComposeFile": [ 4 | "docker-compose.yml" 5 | ], 6 | "service": "vscode", 7 | "runServices": [ 8 | "vscode", 9 | "mkdocs" 10 | ], 11 | "mounts": [ 12 | "source=terraform-harbor-vol,target=/home/vscode/.vscode-server/extensions,type=volume", 13 | // And/or for VS Code Insiders 14 | "source=terraform-harbor-vol-insiders,target=/home/vscode/.vscode-server-insiders/extensions,type=volume", 15 | ], 16 | "shutdownAction": "stopCompose", 17 | "postCreateCommand": "chmod -R 700 .git", 18 | "workspaceFolder": "/go/src/github.com/nolte/terraform-provider-harbor", 19 | // "overrideCommand": "", 20 | "extensions": [ 21 | // General backend 22 | "mohsen1.prettify-json", 23 | "redhat.vscode-yaml", // Kubernetes and Kedge syntax support 24 | // Go 25 | "ms-vscode.go", 26 | "hbenl.vscode-test-explorer", 27 | "ethan-reesor.vscode-go-test-adapter", 28 | // Console 29 | "IBM.output-colorizer", 30 | // Git 31 | "eamodio.gitlens", 32 | "mhutchie.git-graph", 33 | // Other linters 34 | "davidanson.vscode-markdownlint", 35 | "ms-azuretools.vscode-docker", 36 | // Other helpers 37 | "shardulm94.trailing-spaces", 38 | "Gruntfuggly.todo-tree", 39 | "bierner.emojisense", 40 | "stkb.rewrap", // rewrap comments after n characters on one line 41 | // Other 42 | "jrebocho.vscode-random", // generate random values 43 | "alefragnani.Bookmarks", 44 | "quicktype.quicktype", // Paste JSON as code 45 | "spikespaz.vscode-smoothtype", // smooth cursor animation 46 | "vscode-icons-team.vscode-icons", 47 | // markdown 48 | "jebbs.markdown-extended", 49 | "bierner.github-markdown-preview", 50 | "HashiCorp.terraform", 51 | "ms-kubernetes-tools.kind-vscode", 52 | "ms-kubernetes-tools.vscode-kubernetes-tools", 53 | "jetmartin.bats", 54 | "EditorConfig.EditorConfig", 55 | "lonefy.vscode-js-css-html-formatter", 56 | "richie5um2.vscode-statusbar-json-path", 57 | ], 58 | "settings": { 59 | // General settings 60 | "files.eol": "\n", 61 | "files.encoding": "utf8", 62 | "editor.renderWhitespace": "all", 63 | // Docker 64 | "remote.extensionKind": { 65 | "ms-azuretools.vscode-docker": "workspace" 66 | }, 67 | "[makefile]": { 68 | "editor.insertSpaces": false, 69 | "editor.detectIndentation": false 70 | }, 71 | "go.useLanguageServer": true, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | mkdocs: 5 | build: 6 | context: . 7 | dockerfile: DockerfileDocs 8 | volumes: 9 | - ../:/workspace:ro 10 | entrypoint: mkdocs serve -a 0.0.0.0:8000 11 | ports: 12 | - 8000:8000 13 | vscode: 14 | build: . 15 | volumes: 16 | - ../:/go/src/github.com/nolte/terraform-provider-harbor 17 | # (only for debug) 18 | # - ~/repos-ansible/github/plumbing:/go/src/github.com/nolte/plumbing:cached 19 | - ~/.ssh:/home/vscode/.ssh:ro 20 | - ~/.ssh:/root/.ssh:ro 21 | - /var/run/docker.sock:/var/run/docker.sock 22 | ## reuse volumes for extensions 23 | - terraform-harbor-vol:/home/vscode/.vscode-server/extensions 24 | # And/or for VS Code Insiders 25 | - terraform-harbor-vol-insiders:/home/vscode/.vscode-server-insiders/extensions 26 | environment: 27 | - TZ=Europe/Berlin # $(cat /etc/timezone) 28 | cap_add: 29 | - SYS_PTRACE 30 | security_opt: 31 | - seccomp:unconfined 32 | entrypoint: zsh -c "while sleep 1000; do :; done" 33 | network_mode: "host" 34 | 35 | volumes: 36 | terraform-harbor-vol: 37 | terraform-harbor-vol-insiders: 38 | -------------------------------------------------------------------------------- /.devcontainer/files/devops-welcome.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Golang Terraform Harbor Provider Env:" 3 | -------------------------------------------------------------------------------- /.devcontainer/files/devops-zshrc-specific: -------------------------------------------------------------------------------- 1 | plugins=(vscode git colorize docker docker-compose kubetail kubectl terraform pass) 2 | source <(kubectl completion zsh) 3 | NPM_PACKAGES="${HOME}/.npm-packages" 4 | export PATH="$PATH:$NPM_PACKAGES/bin" 5 | # Preserve MANPATH if you already defined it somewhere in your config. 6 | # Otherwise, fall back to `manpath` so we can inherit from `/etc/manpath`. 7 | export MANPATH="${MANPATH-$(manpath)}:$NPM_PACKAGES/share/man" -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [docs/**.md] 13 | # allow line break https://3os.org/markdownCheatSheet/lineBreaks/#line_break_with_2_spaces 14 | trim_trailing_whitespace = false 15 | 16 | [{makefile,Makefile}] 17 | indent_style = tabs 18 | 19 | [{*.yml,*.yaml, *.tf}] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: "If something isn't working as expected \U0001F914." 4 | title: '' 5 | labels: bug 6 | 7 | --- 8 | 9 | 14 | 15 | 16 | ### Terraform Version and Provider Version 17 | 18 | 19 | ### Harbor Version 20 | 21 | 22 | ### Affected Resource(s) 23 | 28 | 29 | ### Terraform Configuration Files 30 | ```hcl 31 | # Copy-paste your Terraform configurations here. 32 | ``` 33 | 34 | ### Debug Output 35 | 36 | 37 | ### Panic Output 38 | 39 | 40 | ### Expected Behavior 41 | What should have happened? 42 | 43 | ### Actual Behavior 44 | What actually happened? 45 | 46 | ### Steps to Reproduce 47 | 49 | 50 | ### Important Factoids 51 | 52 | 53 | ### References 54 | 55 | - GH-1234 56 | 57 | ### Community Note 58 | 59 | * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request 60 | * If you are interested in working on this issue or have submitted a pull request, please leave a comment 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: "I have a suggestion (and might want to implement myself \U0001F642)!" 4 | title: '' 5 | labels: enhancement 6 | 7 | --- 8 | 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Potential Terraform Configuration 15 | 16 | 17 | 18 | ```hcl 19 | # Copy-paste your Terraform configurations here. 20 | ``` 21 | 22 | ### References 23 | 24 | 29 | 30 | 31 | 32 | ### Community Note 33 | 34 | * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request 35 | * If you are interested in working on this issue or have submitted a pull request, please leave a comment 36 | 37 | 38 | -------------------------------------------------------------------------------- /.github/boring-cyborg.yml: -------------------------------------------------------------------------------- 1 | labelPRBasedOnFilePath: 2 | cicd: 3 | - .github/workflows/** 4 | project-config: 5 | - .github/settings.yml 6 | chore: 7 | - .github/** 8 | documentations: 9 | - documentation/** 10 | - mkdocs.yml 11 | - .github/ISSUE_TEMPLATE/** 12 | - .github/pull_request_template.md 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "gomod" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/issue_label_bot.yaml: -------------------------------------------------------------------------------- 1 | label-alias: 2 | bug: 'kind/bug' 3 | feature_request: 'enhancement' 4 | question: 'question' 5 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | 5 | ### Acceptance tests 6 | - [ ] Have you added an acceptance test for the functionality being added? 7 | - [ ] Have you run the acceptance tests on this branch? 8 | 9 | 13 | ### Documentation 14 | - [ ] Have you create or updated the provider documentation at ``./documentation/provider_doc``? 15 | - [ ] If **new** resources or datasource documentation happen, did you add this to the `mkdocs.yml` configuration? 16 | 17 | ### References 18 | 19 | 22 | ### Community Note 23 | 24 | * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request 25 | * If you are interested in working on this issue or have submitted a pull request, please leave a comment 26 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: v$NEXT_PATCH_VERSION 2 | tag-template: v$NEXT_PATCH_VERSION 3 | branches: 4 | - master 5 | - develop 6 | categories: 7 | - title: 🚀 Features 8 | label: enhancement 9 | - title: 🐛 Bug Fixes 10 | label: bug 11 | - title: 🧰 Maintenance 12 | labels: 13 | - 'chore' 14 | - 'documentations' 15 | - 'project-config' 16 | - 'cicd' 17 | - 'dependencies' 18 | 19 | template: | 20 | ## Changes 21 | 22 | $CHANGES 23 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by https://probot.github.io/apps/settings/ 2 | 3 | repository: 4 | # See https://developer.github.com/v3/repos/#edit for all available settings. 5 | 6 | # The name of the repository. Changing this will rename the repository 7 | name: terraform-provider-harbor 8 | 9 | # A short description of the repository that will show up on GitHub 10 | description: Harbor Terraform Provider 11 | 12 | # A URL with more information about the repository 13 | homepage: https://nolte.github.io/terraform-provider-harbor 14 | 15 | # A comma-separated list of topics to set on the repository 16 | topics: terraform, terraform-provider, harbor, goharbor 17 | 18 | # Either `true` to make the repository private, or `false` to make it public. 19 | private: false 20 | 21 | # Either `true` to enable issues for this repository, `false` to disable them. 22 | has_issues: true 23 | 24 | # Either `true` to enable projects for this repository, or `false` to disable them. 25 | # If projects are disabled for the organization, passing `true` will cause an API error. 26 | has_projects: true 27 | 28 | # Either `true` to enable the wiki for this repository, `false` to disable it. 29 | has_wiki: true 30 | 31 | # Either `true` to enable downloads for this repository, `false` to disable them. 32 | has_downloads: true 33 | 34 | # Updates the default branch for this repository. 35 | default_branch: develop 36 | 37 | # Either `true` to allow squash-merging pull requests, or `false` to prevent 38 | # squash-merging. 39 | allow_squash_merge: true 40 | 41 | # Either `true` to allow merging pull requests with a merge commit, or `false` 42 | # to prevent merging pull requests with merge commits. 43 | allow_merge_commit: true 44 | 45 | # Either `true` to allow rebase-merging pull requests, or `false` to prevent 46 | # rebase-merging. 47 | allow_rebase_merge: true 48 | 49 | delete_branch_on_merge: true 50 | 51 | # Labels: define labels for Issues and Pull Requests 52 | labels: 53 | - name: bug 54 | color: CC0000 55 | description: An issue with the system. 56 | 57 | - name: enhancement 58 | # If including a `#`, make sure to wrap it with quotes! 59 | color: '#336699' 60 | description: New functionality. 61 | 62 | - name: chore 63 | color: '#6e70ef' 64 | description: Maintenance 65 | 66 | - name: question 67 | color: '#3ffc8a' 68 | description: User question 69 | 70 | - name: documentations 71 | color: '#caa7e8' 72 | description: Update documentation 73 | 74 | - name: cicd 75 | color: '#fbca04' 76 | description: ci/cd process functionality. 77 | 78 | - name: project-config 79 | color: '#ed979d' 80 | description: Change GitHub project config, keep a extra eye on merge. 81 | 82 | - name: breaking-change 83 | color: '#fc415d' 84 | description: Planed braking provider change. 85 | 86 | - name: duplicate 87 | color: '#cfd3d7' 88 | description: This issue or pull request already exists. 89 | 90 | - name: dependencies 91 | color: '#0366d6' 92 | description: Pull requests that update a dependency file. 93 | 94 | 95 | branches: 96 | - name: develop 97 | # https://developer.github.com/v3/repos/branches/#update-branch-protection 98 | # Branch Protection settings. Set to null to disable 99 | protection: 100 | # Required. Require at least one approving review on a pull request, before merging. Set to null to disable. 101 | required_pull_request_reviews: 102 | required_approving_review_count: 1 103 | # Required. Require status checks to pass before merging. Set to null to disable 104 | required_status_checks: 105 | # Required. Require branches to be up to date before merging. 106 | strict: true 107 | - name: master 108 | # https://developer.github.com/v3/repos/branches/#update-branch-protection 109 | # Branch Protection settings. Set to null to disable 110 | protection: 111 | # Required. Require at least one approving review on a pull request, before merging. Set to null to disable. 112 | required_pull_request_reviews: 113 | # The number of approvals required. (1-6) 114 | required_approving_review_count: 1 115 | # Dismiss approved reviews automatically when a new commit is pushed. 116 | dismiss_stale_reviews: true 117 | # Blocks merge until code owners have reviewed. 118 | require_code_owner_reviews: true 119 | # Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories. 120 | dismissal_restrictions: 121 | users: [] 122 | teams: [] 123 | # Required. Require status checks to pass before merging. Set to null to disable 124 | required_status_checks: 125 | # Required. Require branches to be up to date before merging. 126 | strict: true 127 | # Required. The list of status checks to require in order to merge into this branch 128 | contexts: [] 129 | # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable. 130 | enforce_admins: true 131 | # Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable. 132 | restrictions: 133 | apps: [] 134 | users: [] 135 | teams: [] -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/cicd-docs.yml: -------------------------------------------------------------------------------- 1 | name: Release Flow 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | jobs: 8 | docs: 9 | name: Publish the HTML Documentation 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout master 13 | uses: actions/checkout@v1 14 | - name: Set up Python 15 | uses: actions/setup-python@v2 16 | with: 17 | python-version: "3.8" 18 | - name: Set up Go 1.x 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: ^1.14 22 | id: go 23 | 24 | - uses: actions/cache@v2 25 | with: 26 | path: ~/go/pkg/mod 27 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 28 | restore-keys: | 29 | ${{ runner.os }}-go- 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install MarkdownPP mkdocs 34 | - name: Install dependencies 35 | run: | 36 | cd ./documentation && go run mage.go -v GenerateDocumation 37 | #- name: Deploy docs 38 | # uses: mhausenblas/mkdocs-deploy-gh-pages@master 39 | # env: 40 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/cicd-release.yml: -------------------------------------------------------------------------------- 1 | name: Release Flow 2 | 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | publish_docs: 8 | name: Publish the HTML Documentation 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout master 12 | uses: actions/checkout@v2.3.2 13 | 14 | - name: Set up Go 1.x 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: ^1.14 18 | id: go 19 | 20 | - name: Deploy docs 21 | uses: mhausenblas/mkdocs-deploy-gh-pages@master 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/devcontainers.yml: -------------------------------------------------------------------------------- 1 | name: devcontainers 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | paths: 7 | - ".devcontainer/Dockerfile**" 8 | - .github/workflows/devcontainers.yml 9 | push: 10 | branches: 11 | - "**" 12 | paths: 13 | - ".devcontainer/Dockerfile**" 14 | - .github/workflows/devcontainers.yml 15 | 16 | jobs: 17 | devcontainers-check: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: azure/docker-login@v1 22 | with: 23 | login-server: docker.pkg.github.com 24 | username: ${{ github.actor }} 25 | password: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - name: buid vscode dev image 28 | run: | 29 | cd .devcontainer 30 | docker build -f Dockerfile . 31 | docscontainers-check: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v2 35 | 36 | - name: buid docs image 37 | run: | 38 | cd .devcontainer 39 | docker build -f DockerfileDocs . 40 | -------------------------------------------------------------------------------- /.github/workflows/gorelease.yaml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | goreleaser: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Set up Go 1.x 13 | uses: actions/setup-go@v2 14 | with: 15 | go-version: ^1.14 16 | id: go 17 | 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Import GPG key 24 | id: import_gpg 25 | uses: crazy-max/ghaction-import-gpg@v3 26 | with: 27 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 28 | passphrase: ${{ secrets.PASSPHRASE }} 29 | 30 | - name: test provider 31 | run: | 32 | make generate 33 | 34 | - name: Run GoReleaser 35 | uses: goreleaser/goreleaser-action@v2 36 | with: 37 | version: latest 38 | args: release --rm-dist 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 42 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - develop 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | # with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | # config-name: my-config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: shellcheck 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | paths: 7 | - "**.sh" 8 | - .github/workflows/shellcheck.yml 9 | push: 10 | branches: 11 | - "**" 12 | paths: 13 | - "**.sh" 14 | - .github/workflows/shellcheck.yml 15 | 16 | jobs: 17 | shellcheck-check: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Lint check 23 | uses: azohra/shell-linter@latest 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform-provider-harbor_* 2 | gen 3 | bin 4 | dist 5 | docs 6 | 7 | # Local .terraform directories 8 | **/.terraform/* 9 | 10 | # .tfstate files 11 | *.tfstate 12 | *.tfstate.* 13 | 14 | # Crash log files 15 | crash.log 16 | 17 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 18 | # .tfvars files are managed as part of configuration and so should be included in 19 | # version control. 20 | # 21 | # example.tfvars 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # 32 | # !example_override.tf 33 | 34 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 35 | # example: *tfplan* 36 | 37 | # Ignore CLI configuration files 38 | .terraformrc 39 | terraform.rc 40 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - bodyclose 5 | - deadcode 6 | - depguard 7 | - dogsled 8 | - errcheck 9 | - funlen 10 | - gochecknoinits 11 | - goconst 12 | - gocritic 13 | - gocyclo 14 | - gofmt 15 | - goimports 16 | - golint 17 | - gomnd 18 | - goprintffuncname 19 | - gosec 20 | - gosimple 21 | - govet 22 | - ineffassign 23 | - interfacer 24 | - lll 25 | - misspell 26 | - nakedret 27 | - nolintlint 28 | - rowserrcheck 29 | - scopelint 30 | - staticcheck 31 | - structcheck 32 | - stylecheck 33 | - typecheck 34 | - unconvert 35 | - unparam 36 | - unused 37 | - varcheck 38 | - whitespace 39 | - asciicheck 40 | - gochecknoglobals 41 | - gocognit 42 | - godot 43 | - godox 44 | - maligned 45 | - prealloc 46 | - testpackage 47 | - wsl 48 | # don't enable: 49 | #- nestif 50 | #- dupl 51 | #- goerr113 52 | issues: 53 | # Excluding configuration per-path, per-linter, per-text and per-source 54 | exclude-rules: 55 | # Exclude some linters from running on tests files. 56 | - path: _test\.go 57 | linters: 58 | - gochecknoglobals 59 | - gochecknoinits 60 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | before: 4 | hooks: 5 | - go mod tidy 6 | builds: 7 | - env: 8 | - CGO_ENABLED=0 9 | mod_timestamp: '{{ .CommitTimestamp }}' 10 | flags: 11 | - -trimpath 12 | ldflags: 13 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 14 | goos: 15 | - freebsd 16 | - windows 17 | - linux 18 | - darwin 19 | goarch: 20 | - amd64 21 | - '386' 22 | - arm 23 | - arm64 24 | ignore: 25 | - goos: darwin 26 | goarch: '386' 27 | binary: '{{ .ProjectName }}_v{{ .Version }}' 28 | signs: 29 | - artifacts: checksum 30 | args: ["--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}"] 31 | archives: 32 | - format: zip 33 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 34 | checksum: 35 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 36 | algorithm: sha256 37 | release: 38 | draft: false 39 | changelog: 40 | skip: true 41 | -------------------------------------------------------------------------------- /.spelling: -------------------------------------------------------------------------------- 1 | # markdown-spellcheck spelling configuration file 2 | # Format - lines beginning # are comments 3 | # global dictionary is at the start, file overrides afterwards 4 | # one word per line, to define a file override use ' - filename' 5 | # where filename is relative to this configuration file 6 | int 7 | harbor 8 | harbor_project 9 | harbor_registry 10 | devcontainer 11 | nolte 12 | github.com 13 | kubeconfig 14 | goharbor 15 | hostname 16 | third-party-plugins 17 | harbor_config_system 18 | oidc 19 | openid 20 | ci 21 | mkdocs 22 | kubernetesindocker 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=1.0 2 | 3 | # helper to run go from project root 4 | GO := go 5 | 6 | # generate harbor cient files from swagger config 7 | define install_provider 8 | mkdir -p ~/.terraform.d/plugins/linux_amd64/ 9 | cp ./dist/terraform-provider-harbor_linux_amd64/terraform-provider-harbor_v* ~/.terraform.d/plugins/linux_amd64/ 10 | chmod +x ~/.terraform.d/plugins/linux_amd64/terraform-provider-harbor_v* 11 | endef 12 | 13 | TEST?=$$(go list ./... |grep -v 'vendor') 14 | GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) 15 | MAKEFLAGS += --silent 16 | 17 | default: build 18 | 19 | generate: 20 | cd ./tools && go run mage.go -v build:generateHarborGoClient 21 | 22 | compile: 23 | # scripts/build-10-compile.sh 24 | cd ./tools && go run mage.go -v build:goRelease 25 | 26 | install: 27 | cd ./tools && go run mage.go -v build:TerraformInstallProvider 28 | 29 | build: generate test compile 30 | 31 | fmt: 32 | echo "==> Formatting files with fmt..." 33 | cd ./tools && go run mage.go -v build:fmt 34 | 35 | test: goLint scriptsLint vet 36 | go test $(TEST) 37 | 38 | fmtcheck: 39 | cd ./tools && go run mage.go -v build:fmt 40 | 41 | vet: 42 | echo "==> Checking code with vet..." 43 | go vet ./... 44 | 45 | goLint: 46 | fmt 47 | scripts/build-04-go-errorchecks.sh 48 | cd ./tools && go run mage.go -v lint || true 49 | 50 | gosec: 51 | echo "==> Checking code with gosec..." 52 | # TODO Remove unused files from generated sources !!! 53 | gosec -exclude-dir=gen/harborctl/client/scanners ./... 54 | 55 | scriptsLint: 56 | echo "==> Checking scripts with shellcheck..." 57 | shellcheck scripts/*.sh 58 | 59 | e2e_prepare: 60 | cd ./tools && go run mage.go -v kind:recreate 61 | 62 | e2e_prepare_harbor_v1: 63 | cd ./tools && HARBOR_HELM_CHART_VERSION=1.3.2 go run mage.go -v testArtefacts:deploy 64 | 65 | e2e_prepare_harbor_v2: 66 | cd ./tools && HARBOR_HELM_CHART_VERSION=1.4.0 go run mage.go -v testArtefacts:deploy 67 | 68 | e2e_prepare_harbor_v2_1: 69 | cd ./tools && HARBOR_HELM_CHART_VERSION=1.4.1 go run mage.go -v testArtefacts:deploy 70 | 71 | e2e_clean_cluster: 72 | kind delete cluster || true 73 | 74 | e2e_clean_harbor: 75 | cd ./tools && go run mage.go -v testArtefacts:delete 76 | sleep 10 77 | 78 | e2e_test_v2: 79 | scripts/tst-15-execute-go-acc.sh "/api/v2.0" 80 | 81 | e2e_test_v1: 82 | scripts/tst-15-execute-go-acc.sh "/api" 83 | 84 | e2e_test_classic: 85 | scripts/tst-15-execute-classic-acc.sh "/api/v2.0" 86 | 87 | e2e_full_run: e2e_clean_cluster e2e_prepare e2e_prepare_harbor_v2 e2e_test_v2 e2e_clean_harbor e2e_prepare_harbor_v1 e2e_test_v1 e2e_clean_cluster 88 | # e2e_prepare e2e_prepare_harbor_v1 e2e_test e2e_cleanup 89 | spellingCheck: 90 | mdspell '**/*.md' '!**/node_modules/**/*.md' 91 | 92 | .PHONY: default install 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Harbor Provider 2 | 3 | The `terraform-provider-harbor` is used to configure an instance of [Harbor](https://goharbor.io). 4 | 5 | This is original based on the Work from [BESTSELLER/terraform-harbor-provider](https://github.com/BESTSELLER/terraform-harbor-provider), but with some incompatible changes, like the access to the Harbor API. 6 | 7 | [![Classic CI/CD](https://github.com/nolte/terraform-provider-harbor/workflows/Classic%20CI/CD/badge.svg)](https://github.com/nolte/terraform-provider-harbor/actions?query=workflow%3A%22Classic+CI%2FCD%22) 8 | [![Release Flow](https://github.com/nolte/terraform-provider-harbor/workflows/Release%20Flow/badge.svg)](https://github.com/nolte/terraform-provider-harbor/actions?query=workflow%3A%22Release+Flow%22) 9 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnolte%2Fterraform-provider-harbor.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnolte%2Fterraform-provider-harbor?ref=badge_shield) 10 | 11 | ## Project Status 12 | 13 | **At the Moment this Project is heavy under Construction, it is not recommended for Production use, ~~or active Forking~~ !** 14 | 15 | **Planed Braking Changes:** 16 | 17 | * [x] [Rename provider](https://github.com/nolte/terraform-provider-harbor/issues/3) attributes, like url etc. 18 | * [ ] [Refectore Config Auth](https://github.com/nolte/terraform-provider-harbor/issues/10) attributes, like ldap etc. 19 | * [ ] Use First Stable version from the Devcontainer [docker.pkg.github.com/nolte/vscode-devcontainers/k8s-operator:latest](https://github.com/nolte/vscode-devcontainers) _(not exists at the moment)_ 20 | 21 | ## Docs 22 | 23 | The Documentation will be created with [mkdocs](https://www.mkdocs.org/) and generated to [nolte.github.io/terraform-provider-harbor](https://nolte.github.io/terraform-provider-harbor/) from the latest Release like, 24 | 25 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nolte/terraform-provider-harbor) 26 | 27 | . 28 | 29 | ## Building 30 | 31 | As CI/CD tool we use the Github Workflow Feature. 32 | 33 | ## Visual Studio Code DevContainer 34 | 35 | For Easy development use [Visual Studio Code DevContainer](https://code.visualstudio.com/docs/remote/containers), you can find the basement from the Development Containers at [nolte/vscode-devcontainers](https://github.com/nolte/vscode-devcontainers). 36 | 37 | 1. Create you Github Personal Access Token under with the following scopes: 38 | 39 | 1. `read:packages` 40 | 2. Login to fetch the required dev containers 41 | 42 | ``` sh 43 | docker login docker.pkg.github.com 44 | ``` 45 | 46 | 3. Grab you a Coffee and wait for 3 Minutes (This happens on the first time use) 47 | 48 | 4. Click Terminal -> New Terminal and execute the following command: 49 | 50 | ``` sh 51 | # using the Makefile 52 | make 53 | ``` 54 | 55 | After starting the VisualStudio Code DevContainer, you can access the Documentation at [localhost:8000](http://localhost:8000). 56 | 57 | ### Precondition Tools 58 | 59 | For full building and testing you need the following tools on our machine. 60 | 61 | #### Required For Building 62 | 63 | * go 64 | * [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger) 65 | * [evanphx/json-patch](https://github.com/evanphx/json-patch) 66 | 67 | #### Required For Testing 68 | 69 | * kind 70 | * Terraform 71 | * [gruntwork-io/terratest](https://github.com/gruntwork-io/terratest) 72 | 73 | ## Supported Versions 74 | 75 | Tested with Harbor v1.10.2, v2.0.0 and ~v2.1.0~ (currently broken [#83](https://github.com/nolte/terraform-provider-harbor/pull/83)). 76 | 77 | ## Tests 78 | 79 | For the End To End Tests we use a local [kind](https://kind.sigs.k8s.io) _(KubernetesInDocker)_ Cluster. 80 | 81 | ## License 82 | 83 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnolte%2Fterraform-provider-harbor.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnolte%2Fterraform-provider-harbor?ref=badge_large) 84 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "crypto/tls" 5 | "log" 6 | "net/http" 7 | 8 | httptransport "github.com/go-openapi/runtime/client" 9 | "github.com/go-openapi/strfmt" 10 | 11 | apiclient "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 12 | ) 13 | 14 | // NewClient creates common settings. 15 | func NewClient(host string, username string, password string, 16 | insecure bool, basepath string, schema string) *apiclient.Harbor { 17 | log.Printf("Create Harbor Api HTTP Client for %s:XXXX@%s insecure %v baspath: %s", username, host, insecure, basepath) 18 | 19 | basicAuth := httptransport.BasicAuth(username, password) 20 | 21 | // allow skipping ssl 22 | // #nosec 23 | http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ 24 | InsecureSkipVerify: insecure, 25 | } 26 | 27 | apiSchemes := make([]string, 1) 28 | apiSchemes[0] = schema 29 | 30 | transport := httptransport.New(host, basepath, apiSchemes) 31 | 32 | // add default auth 33 | transport.DefaultAuthentication = basicAuth 34 | 35 | // create the API client, with the transport 36 | client := apiclient.New(transport, strfmt.Default) 37 | 38 | return client 39 | } 40 | -------------------------------------------------------------------------------- /docs/data-sources/harbor_label.md: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_label 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | data "harbor_label" "label_1" { 7 | name = "testlabel-acc-classic" 8 | scope = "g" 9 | } 10 | 11 | data "harbor_label" "label_2" { 12 | id = 4 13 | } 14 | 15 | ``` 16 | 17 | ## Argument Reference 18 | 19 | - `id` - (Optional, int) ID of the label. 20 | - `name` - (Optional, string) Name of the label. 21 | - `scope` - (Optional, string) Scope of the label. 22 | 23 | ## Attributes Reference 24 | 25 | - `id` - (int) Unique ID of the label. 26 | - `name` - (string) Name of the label. 27 | - `description` - (Optional) The description of the label account will be displayed in harbor. 28 | - `color` - (Optional) The colour the label. 29 | - `scope` - (Optional) The scope the label, `p` for project and `g` for global. 30 | - `project_id` - (Optional) The ID of project that the label belongs to. 31 | -------------------------------------------------------------------------------- /docs/data-sources/harbor_project.md: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_project 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | data "harbor_project" "project_1" { 7 | name = "main" 8 | } 9 | 10 | data "harbor_project" "project_2" { 11 | id = 4 12 | } 13 | 14 | ``` 15 | 16 | ## Argument Reference 17 | 18 | - `id` - (Optional, int) ID of the project. 19 | - `name` - (Optional, string) Name of the project. 20 | 21 | ## Attributes Reference 22 | 23 | - `id` - (int) Unique ID of the project. 24 | - `name` - (string) Name of the project. 25 | -------------------------------------------------------------------------------- /docs/data-sources/harbor_registry.md: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_registry 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | data "harbor_registry" "registry_1" { 7 | name = "dockerhub-acc-classic" 8 | } 9 | 10 | data "harbor_registry" "registry_2" { 11 | id = 4 12 | } 13 | 14 | ``` 15 | 16 | ## Argument Reference 17 | 18 | - `id` - (Optional, string) ID of the registry. 19 | - `name` - (Optional, string) Name of the registry. 20 | 21 | ## Attributes Reference 22 | 23 | - `id` - (int) Unique ID of the registry. 24 | - `name` - (string) Name of the registry. 25 | -------------------------------------------------------------------------------- /docs/resources/harbor_config_system.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "harbor" 3 | #subcategory: "config" 4 | page_title: "Harbor: harbor_config_system" 5 | description: |- 6 | Manages an Base Harbor System Configuration 7 | --- 8 | 9 | # Resource: harbor_config_system 10 | 11 | ## Example Usage 12 | 13 | ```hcl 14 | resource "harbor_config_system" "main" { 15 | project_creation_restriction = "adminonly" 16 | robot_token_expiration = 5259492 17 | } 18 | ``` 19 | 20 | ## Argument Reference 21 | The following arguments are supported: 22 | 23 | * **project_creation_restriction** - (Optional) Who can create projects within Harbor. Can be **"adminonly"** or **"everyone"** 24 | 25 | * **robot_token_expiration** - (Optional) The amount of time in minutes a robot account will expiry. 26 | 27 | `NOTE: If the time is set to high you will get a 500 internal server error message when creating robot accounts` 28 | -------------------------------------------------------------------------------- /docs/resources/harbor_configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_config_auth" 4 | description: |- 5 | Manages an Base Harbor Auth Configuration 6 | --- 7 | 8 | 9 | # Resource: harbor_config_auth 10 | 11 | ## Example Usage 12 | 13 | ```hcl 14 | resource "harbor_config_auth" "oidc" { 15 | auth_mode = "oidc_auth" 16 | oidc_name = "azure" 17 | oidc_endpoint = "https://login.microsoftonline.com/{GUID goes here}/v2.0" 18 | oidc_client_id = "OIDC Client ID goes here" 19 | oidc_client_secret = "ODDC Client Secret goes here" 20 | oidc_scope = "openid,email" 21 | oidc_verify_cert = true 22 | } 23 | ``` 24 | 25 | ## Argument Reference 26 | The following arguments are supported: 27 | 28 | * **auth_mode** - (Required) Harbor authentication mode. Can be **"oidc_auth"** or **"db_auth"**. (Default: **"db_auth"**) 29 | 30 | * **oidc_name** - (Optional) The name of the oidc provider name. (Required - if auth_mode set to **oidc_auth**) 31 | 32 | * **oidc_endpoint** - (Optional) The URL of an OIDC-complaint server. (Required - if auth_mode set to **oidc_auth**) 33 | 34 | * **oidc_client_id** - (Optional) The client id for the oidc server. (Required - if auth_mode set to **oidc_auth**) 35 | 36 | * **oidc_client_serect** - (Optional) The client secret for the oidc server. (Required - if auth_mode set to **oidc_auth**) 37 | 38 | * **oidc_groups_claim** - (Optional) The name of the claim in the token whose values is the list of group names. 39 | 40 | `NOTE: "oidc_groups_claim" can only be used with harbor version v1.10.1 and above` 41 | 42 | * **oidc_scope** - (Optional) The scope sent to OIDC server during authentication. It has to contain “openid”. (Required - if auth_mode set to **oidc_auth**) 43 | 44 | * **oidc_verify_cert** - (Optional) Set to **"false"** if your OIDC server is using a self-signed certificate. (Required - if auth_mode set to **oidc_auth**) 45 | -------------------------------------------------------------------------------- /docs/resources/harbor_label.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_label 2 | 3 | Harbor Doc: [Managing Labels](https://goharbor.io/docs/1.10/working-with-projects/working-with-images/create-labels/) 4 | Harbor Api: [/labels](https://demo.goharbor.io/#/Products/post_labels) 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | resource "harbor_label" "main" { 10 | name = "testlabel-acc-classic" 11 | description = "Test Label" 12 | color = "#61717D" 13 | scope = "g" 14 | } 15 | 16 | data "harbor_project" "project_1" { 17 | name = "main" 18 | } 19 | 20 | resource "harbor_label" "project_label" { 21 | name = "projectlabel-acc-classic" 22 | description = "Test Label for Project" 23 | color = "#333333" 24 | scope = "p" 25 | project_id = data.harbor_project.project_1.id 26 | } 27 | ``` 28 | 29 | ## Argument Reference 30 | 31 | The following arguments are required: 32 | 33 | * `name` - (Required) Name of the Project. 34 | 35 | The following arguments are optional: 36 | 37 | * `description` - (Optional) The description of the label account will be displayed in harbor. 38 | 39 | * `color` - (Optional) The colour the label. 40 | 41 | * `scope` - (Optional) The scope the label, `p` for project and `g` for global. 42 | 43 | * `project_id` - (Optional) The ID of project that the label belongs to, must be set if scope project. 44 | 45 | ## Attributes Reference 46 | 47 | In addition to all argument, the following attributes are exported: 48 | 49 | * `id` - The id of the registry with harbor. 50 | 51 | ## Import 52 | 53 | Harbor Projects can be imported using the `harbor_label`, e.g. 54 | 55 | ```sh 56 | terraform import harbor_label.helmhub 1 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/resources/harbor_project.md: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_project" 4 | description: |- 5 | Manages an Harbor Project 6 | --- 7 | 8 | # Resource: harbor_project 9 | 10 | Handle a [Harbor Project Resource](https://goharbor.io/docs/1.10/working-with-projects/create-projects/). 11 | 12 | ## Example Usage 13 | 14 | ```hcl 15 | resource "harbor_project" "main" { 16 | name = "main" 17 | public = false # (Optional) Default value is false 18 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 19 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 20 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are required: 27 | 28 | * `name` - (Required) Name of the Project. 29 | 30 | The following arguments are optional: 31 | 32 | * `public` - (Optional) Handle the access to the hosted images. Default: `true` 33 | 34 | If `true` Any user can pull images from this project. This is a convenient way for you to share repositories with others. 35 | 36 | If `false` Only users who are members of the project can pull images 37 | 38 | * `vulnerability_scanning` - (Optional) Activate [Vulnerability Scanning](https://goharbor.io/docs/1.10/administration/vulnerability-scanning/). Default: `true` 39 | 40 | * `reuse_sys_cve_whitelist` - (Optional) Whether this project should reuse the system level CVE whitelist as the whitelist of its own. Default: `true` 41 | 42 | If `true` The whitelist associated with this project will be ignored. 43 | 44 | If `false` The project will use the whitelist defined by `cve_whitelist`. 45 | 46 | * `cve_whitelist` - (Optional) List of whitelisted CVE ids for the project. 47 | 48 | ## Attributes Reference 49 | 50 | In addition to all arguments above, the following attributes are exported: 51 | 52 | * `id` - Harbor Project ID. 53 | 54 | ## Import 55 | 56 | Harbor Projects can be imported using the `harbor_project`, e.g. 57 | 58 | ``` 59 | $ terraform import harbor_project.main 1 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/resources/harbor_project_member.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_project_member 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | resource "harbor_project" "main" { 7 | name = "main" 8 | public = false # (Optional) Default value is false 9 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 10 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 11 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 12 | } 13 | 14 | resource "harbor_usergroup" "developers" { 15 | name = "developers" 16 | type = "http" 17 | } 18 | 19 | resource "harbor_project_member" "developers_main" { 20 | project_id = harbor_project.main.id 21 | role = "guest" 22 | group_type = "http" 23 | group_name = harbor_usergroup.developers.name 24 | } 25 | ``` 26 | 27 | ## Argument Reference 28 | 29 | The following arguments are supported: 30 | 31 | * `role` - (Required) The role permission to be given to the group: `project_admin`, `master`, `developer`, `guest` or `limited_guest`. 32 | 33 | * `group_type` - (Required) The group type: `ldap`, `http` or `oidc`. 34 | 35 | * `project_id` - (Required) The project id of the project to be given permission to the group members. 36 | 37 | * `group_name` - (Required) The name of the user group to be given permissions for the project. 38 | 39 | ## Attributes Reference 40 | 41 | In addition to all arguments, the following attribute is exported: 42 | 43 | * `id` - The id of the project account: format is `${project_id}/${group_id}`. 44 | 45 | ## Import 46 | 47 | Harbor Project member can be imported using the `harbor_project_member`, e.g. 48 | 49 | ```sh 50 | terraform import harbor_project_member.developers_main 12/5 51 | ``` 52 | 53 | ## Known limitations 54 | 55 | The provider currently only handles group membership, not user. 56 | -------------------------------------------------------------------------------- /docs/resources/harbor_registry.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_registry 2 | 3 | Harbor Doc: [managing-registries](https://goharbor.io/docs/2.0.0/administration/configuring-replication/create-replication-endpoints/#managing-registries) 4 | Harbor Api: [Create](https://demo.goharbor.io/#/Products/post_registries) 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | resource "harbor_registry" "dockerhub" { 10 | name = "dockerhub-acc-classic" 11 | url = "https://hub.docker.com" 12 | type = "docker-hub" 13 | description = "Docker Hub Registry" 14 | insecure = false 15 | } 16 | 17 | resource "harbor_registry" "helmhub" { 18 | name = "helmhub-acc-classic" 19 | url = "https://hub.helm.sh" 20 | type = "helm-hub" 21 | description = "Helm Hub Registry" 22 | insecure = false 23 | } 24 | ``` 25 | 26 | ## Argument Reference 27 | 28 | The following arguments are supported: 29 | 30 | * `name` - (Required) of the project that will be created in harbor. 31 | 32 | * `url` - (Required) The registry remote endpoint, like `https://hub.docker.com`. 33 | 34 | * `type` - (Required) registry Type possible values are `huawei-SWR, aws-ecr, ali-acr, jfrog-artifactory, gitlab, docker-registry, docker-hub, azure-acr, quay-io, helm-hub, harbor, google-gcr`. 35 | 36 | * `description` - (Optional) The description of the registry will be displayed in harbor. 37 | 38 | * `insecure` - (Optional) Harbor ignores insecure external registry errors. Can be set to `true` or `false` (Default: `false`) 39 | 40 | * `access_key` - (Optional) The registry access key. 41 | 42 | * `access_secret` - (Optional) The registry access secret. 43 | 44 | * `credential_type` - (Optional) Credential type, such as 'basic', 'oauth'. 45 | 46 | ## Attributes Reference 47 | 48 | In addition to all argument, the following attributes are exported: 49 | 50 | * `id` - The id of the registry with harbor. 51 | 52 | ## Import 53 | 54 | Harbor Projects can be imported using the `harbor_registry`, e.g. 55 | 56 | ```sh 57 | terraform import harbor_registry.helmhub 1 58 | ``` 59 | -------------------------------------------------------------------------------- /docs/resources/harbor_replication.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_replication 2 | 3 | Harbor Doc: [configuring-replication](https://goharbor.io/docs/2.0.0/administration/configuring-replication/) 4 | 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | resource "harbor_project" "main" { 10 | name = "main" 11 | public = false # (Optional) Default value is false 12 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 13 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 14 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 15 | } 16 | 17 | resource "harbor_registry" "dockerhub" { 18 | name = "dockerhub-acc-classic" 19 | url = "https://hub.docker.com" 20 | type = "docker-hub" 21 | description = "Docker Hub Registry" 22 | insecure = false 23 | } 24 | 25 | resource "harbor_registry" "helmhub" { 26 | name = "helmhub-acc-classic" 27 | url = "https://hub.helm.sh" 28 | type = "helm-hub" 29 | description = "Helm Hub Registry" 30 | insecure = false 31 | } 32 | 33 | resource "harbor_replication" "pull_helm_chart" { 34 | name = "helm-prometheus-operator-acc-classic" 35 | description = "Prometheus Operator Replica ACC Classic" 36 | source_registry_id = harbor_registry.helmhub.id 37 | source_registry_filter_name = "stable/prometheus-operator" 38 | source_registry_filter_tag = "**" 39 | destination_namespace = harbor_project.main.name 40 | } 41 | 42 | resource "harbor_replication" "push_helm_chart" { 43 | name = "docker-push-acc-classic" 44 | description = "Push Docker Replica ACC Classic" 45 | destination_registry_id = harbor_registry.dockerhub.id 46 | source_registry_filter_name = "${harbor_project.main.name}/vscode-devcontainers/k8s-operator" 47 | source_registry_filter_tag = "**" 48 | destination_namespace = "notexisting" 49 | } 50 | ``` 51 | 52 | ## Argument Reference 53 | 54 | The following arguments are supported: 55 | 56 | * `name` - (Required) of the replication that will be created in harbor. 57 | 58 | * `description` - (Optional) of replication that will be displayed in harbor. 59 | 60 | * `source_registry_id` - (Optional) Used for pull the resources from the remote registry to the local Harbor. 61 | 62 | * `source_registry_filter_name` - (Optional) Filter the name of the resource. Leave empty or use '\*\*' to match all. 'library/\*\*' only matches resources under 'library'. 63 | 64 | * `source_registry_filter_tag` - (Optional) Filter the tag/version part of the resources. Leave empty or use '\*\*' to match all. '1.0*' only matches the tags that starts with '1.0'. 65 | 66 | * `destination_namespace` - (Optional) Destination namespace Specify the destination namespace. If empty, the resources will be put under the same namespace as the source. 67 | 68 | * `destination_registry_id` - (Optional) The target Registry ID, used only for `push-based` replications. 69 | 70 | * `trigger_mode` - (Optional) Can be `manual`,`scheduled` and for push-based addition `event_based`, Default: `manual` 71 | 72 | * `trigger_cron` - (Optional) Used cron for `scheduled` trigger mode, like `* * 5 * * *` 73 | 74 | * `override` - (Optional) Specify whether to override the resources at the destination if a resource with the same name exists. Default: `false` 75 | 76 | * `enabled` - (Optional) 77 | 78 | 79 | ## Attributes Reference 80 | 81 | In addition to all argument, the following attributes are exported: 82 | 83 | * `id` - The id of the registry policy with harbor. 84 | 85 | ## Import 86 | 87 | Harbor Projects can be imported using the `harbor_replication`, e.g. 88 | 89 | ```sh 90 | terraform import harbor_replication.helmhub_prometheus 1 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/resources/harbor_retention_policy.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_retention_policy 2 | 3 | Harbor official documentation feature reference: https://goharbor.io/docs/2.0.0/working-with-projects/working-with-images/create-tag-retention-rules/ 4 | 5 | ## Example Usage 6 | 7 | ```hcl 8 | resource "harbor_project" "main" { 9 | name = "main" 10 | public = false # (Optional) Default value is false 11 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 12 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 13 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 14 | } 15 | 16 | resource "harbor_retention_policy" "cleanup" { 17 | scope { 18 | ref = harbor_project.main.id 19 | } 20 | 21 | rule { 22 | template = "always" 23 | tag_selectors { 24 | decoration = "matches" 25 | pattern = "master" 26 | extras = jsonencode({ 27 | untagged: false 28 | }) 29 | } 30 | scope_selectors { 31 | repository { 32 | kind = "doublestar" 33 | decoration = "repoMatches" 34 | pattern = "**" 35 | } 36 | } 37 | } 38 | 39 | rule { 40 | disabled = true 41 | template = "latestPulledN" 42 | params = { 43 | "latestPulledN" = 15 44 | "nDaysSinceLastPush" = 7 45 | } 46 | tag_selectors { 47 | kind = "doublestar" 48 | decoration = "excludes" 49 | pattern = "master" 50 | extras = jsonencode({ 51 | untagged: false 52 | }) 53 | } 54 | scope_selectors { 55 | repository { 56 | kind = "doublestar" 57 | decoration = "repoExcludes" 58 | pattern = "nginx-*" 59 | } 60 | } 61 | } 62 | 63 | trigger { 64 | settings { 65 | cron = "0 0 0 * * *" 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | ## Argument Reference 72 | 73 | The following arguments are supported: 74 | 75 | * `algorithm` - (Optional) Algorithm used to aggregate rules. Defaults to `"or"`. 76 | 77 | * `scope` - (Required) Describes the (project) scope of this retention policy. 78 | 79 | * `rule` - (Required) List of retention rules that compose the policy. 80 | 81 | * `trigger` - (Required) Defines a schedule with a cron rule. 82 | 83 | ## Nested Blocks 84 | 85 | ### scope 86 | 87 | #### Arguments 88 | 89 | * `level` - (Optional) Defines the level the scope applies to. Defaults to `"project"`. 90 | 91 | * `ref` - (Required) Reference id (usually project id) the retention policy refers to. 92 | 93 | ### rule 94 | 95 | #### Arguments 96 | 97 | * `disabled` - (Optional) Specifies if the rule should be taken in account or not (ie disabled). Defaults to `false`. 98 | 99 | * `action` - (Optional) Specifies the action to do if set of rules is matched. Defaults to `"retain"`. 100 | 101 | * `template` - (Required) kind of rules among the following: 102 | 103 | * `latestPushedK`: (retain) the most recently pushed # artifacts 104 | 105 | * `latestPulledN`: (retain) the most recently pulled # artifacts 106 | 107 | * `nDaysSinceLastPush`: (retain) the artifacts pushed with the last # days 108 | 109 | * `nDaysSinceLastPull`: (retain) the artifacts pulled with the last # days 110 | 111 | * `always`: (retain) always 112 | 113 | * `params` - (Optional) Map of params for the kind of rule (number of days or artifacts associated to the rule template). Defaults to empty map. 114 | 115 | Note that multiple key-values can be specified instead of only the one matching the template. The other ones would be disabled but recorded as default choice to the associated template within the UI combo-box. 116 | 117 | * `tag_selectors` - (Required) Describes the image tags on which to apply the rule. 118 | 119 | * `scope_selectors` - (Required) Describes the image name(s) on which to apply the rule. 120 | 121 | #### Attributes 122 | 123 | * `id` - The id of the rule (currently not used and set to `0` whatever the number of rules). 124 | 125 | * `priority` - A priority tag to know in which order the set of rules should b performed (currently not used and set to `0`). 126 | 127 | ### `tag_selectors` 128 | 129 | #### Arguments 130 | 131 | * `kind` - (Optional) The kind of tag selector. Defaults to `doublestar`. 132 | 133 | * `extras` - (Optional) Describes extra info for tag selector as a json encoded string, supports mainly the following key value: `untagged: (false|true)` to match or not the untagged artifacts in the rule. Defaults to `""`. 134 | 135 | * `decoration` - (Required) Specifies if the pattern should be matched (`"matches"`) or excluded (`"excludes"`). 136 | 137 | * `pattern` - (Required) Describes the pattern that should match (or exclude) image tags 138 | 139 | ### `scope_selectors` 140 | 141 | #### Arguments 142 | 143 | * `kind` - (Optional) The kind of scope selector. Defaults to `doublestar`. 144 | 145 | * `extras` - (Optional) Describes extra info for scope selector as a json encoded string, does not support any known key value as now. Defaults to `""`. 146 | 147 | * `decoration` - (Required) Specifies if the pattern should be matched (`"repoMatches"`) or excluded (`"repoExcludes"`). 148 | 149 | * `pattern` - (Required) Describes the pattern that should match (or exclude) image names 150 | 151 | ### `trigger` 152 | 153 | #### Arguments 154 | 155 | * `kind` - (Optional) The kind of trigger. Defaults to `Schedule`. 156 | 157 | * `settings` - (Required) The trigger settings. 158 | 159 | #### Attributes 160 | 161 | * `references` - Reference to an internal job responsible for applying the trigger. 162 | 163 | ### `settings` 164 | 165 | #### Arguments 166 | 167 | * `cron` - (Optional) 6-fields cron pattern for triggering the retention policy execution by cron schedule. Defaults to `""` (which refers to a "None" Schedule). 168 | 169 | ### `references` 170 | 171 | #### Attributes 172 | 173 | * `job_id` - Reference to the internal job id associated to the trigger. 174 | 175 | ## Attributes Reference 176 | 177 | In addition to all arguments, the following attribute is exported: 178 | 179 | * `id` - The id of the retention policy. 180 | 181 | ## Import 182 | 183 | Harbor Retention policies can be imported using the `harbor_retention_policy`, e.g. 184 | 185 | ```sh 186 | terraform import harbor_retention_policy.cleanup 2 187 | ``` 188 | -------------------------------------------------------------------------------- /docs/resources/harbor_robot_account.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_robot_account 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | resource "harbor_project" "main" { 7 | name = "main" 8 | public = false # (Optional) Default value is false 9 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 10 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 11 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 12 | } 13 | 14 | resource "harbor_robot_account" "master_robot" { 15 | name = "god" 16 | description = "Robot account used to push images to harbor" 17 | project_id = harbor_project.main.id 18 | actions = ["docker_read", "docker_write", "helm_read", "helm_write"] 19 | } 20 | 21 | output "harbor_robot_account_token" { 22 | value = harbor_robot_account.master_robot.token 23 | } 24 | ``` 25 | 26 | ## Argument Reference 27 | 28 | The following arguments are supported: 29 | 30 | * `name` - (Required) The of the project that will be created in harbor. 31 | 32 | * `description` - (Optional) The description of the robot account will be displayed in harbor. 33 | 34 | * `project_id` - (Required) The project id of the project that the robot account will be associated with. 35 | 36 | * `actions` - (Optional) 37 | 38 | ## Attributes Reference 39 | 40 | In addition to all argument, the following attributes are exported: 41 | 42 | * `id` - The id of the robot account. 43 | 44 | * `token` - The token of the robot account. 45 | 46 | ## Import 47 | 48 | Harbor Projects can be imported using the `harbor_robot_account`, e.g. 49 | 50 | ```sh 51 | terraform import harbor_robot_account.master_robot 29 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/resources/harbor_tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_tasks" 4 | description: |- 5 | Manages an Harbor Task 6 | --- 7 | 8 | # Resource: harbor_tasks 9 | 10 | ## Example Usage 11 | 12 | ```hcl 13 | #resource "harbor_tasks" "main" { 14 | # vulnerability_scan_policy = "daily" 15 | #} 16 | ``` 17 | 18 | ## Argument Reference 19 | The following arguments are supported: 20 | 21 | * **vulnerability_scan_policy** - (Optional) The frequency of the vulnerability scanning is done. Can be to **"hourly"**, **"daily"** or **"weekly"** 22 | -------------------------------------------------------------------------------- /docs/resources/harbor_usergroup.md: -------------------------------------------------------------------------------- 1 | # Resource: harbor_usergroup 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | resource "harbor_usergroup" "developers" { 7 | name = "developers" 8 | type = "http" 9 | } 10 | ``` 11 | 12 | 13 | ## Argument Reference 14 | 15 | The following arguments are supported: 16 | 17 | * `name` - (Required) The name of the user group to be created. 18 | 19 | * `ldap_dn` - (Optional) The LDAP Group distingish name if `type` is `ldap`, defaults to `""`. 20 | 21 | * `type` - (Required) The group type: `ldap`, `http` or `oidc`. 22 | 23 | ## Attributes Reference 24 | 25 | In addition to all arguments, the following attribute is exported: 26 | 27 | * `id` - The id of the group. 28 | 29 | ## Import 30 | 31 | Harbor Usergroups can be imported using the `harbor_usergroup`, e.g. 32 | 33 | ```sh 34 | terraform import harbor_usergroup.developers 5 35 | ``` 36 | -------------------------------------------------------------------------------- /documentation/adr/0000-use-markdown-architectural-decision-records.md: -------------------------------------------------------------------------------- 1 | # Use Markdown Architectural Decision Records 2 | 3 | ## Context and Problem Statement 4 | 5 | We want to record architectural decisions made in this project. 6 | Which format and structure should these records follow? 7 | 8 | ## Considered Options 9 | 10 | * [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records 11 | * [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" 12 | * [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements 13 | * Other templates listed at 14 | * Formless – No conventions for file format and structure 15 | 16 | ## Decision Outcome 17 | 18 | Chosen option: "MADR 2.1.2", because 19 | 20 | * Implicit assumptions should be made explicit. 21 | Design documentation is important to enable people understanding the decisions later on. 22 | See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). 23 | * The MADR format is lean and fits our development style. 24 | * The MADR structure is comprehensible and facilitates usage & maintenance. 25 | * The MADR project is vivid. 26 | * Version 2.1.2 is the latest one available when starting to document ADRs. 27 | -------------------------------------------------------------------------------- /documentation/adr/0001-use-swagger-for-generate-http-client.md: -------------------------------------------------------------------------------- 1 | # Need a GoBased ApiClient for Harbor 2 | 3 | * Status: accepted 4 | * Date: 2020-05-01 5 | 6 | ## Context and Problem Statement 7 | 8 | For a quick development from the Terraform Provider Functions, it makes sense to generate or use a HarborRestAPI Client. 9 | 10 | 11 | ## Considered Options 12 | 13 | * [Self Written](#self-written) 14 | * [Existing](#existing) 15 | * [Swagger Based](#swagger-based) 16 | 17 | ## Decision Outcome 18 | 19 | Chosen option: "Swagger Based", because this solution supports the fastes development Start without writting any boilerplate code. 20 | 21 | ### Positive Consequences 22 | 23 | * No Painfull HTTP Client Implementation 24 | 25 | ### Negative Consequences 26 | 27 | * the API Client Implementation dependents to the Swagger Spec Quality... 28 | 29 | ## Pros and Cons of the Options 30 | 31 | ### Self Written 32 | 33 | Using a SelfWritten Api Client like the [BESTSELLER/terraform-harbor-provider](https://github.com/BESTSELLER/terraform-harbor-provider) implementation. 34 | 35 | * 👍, because full flexibility, for api interactions. 36 | * 👍, because no external dependency. 37 | * 👎, because it make no fun to write a API Client, with Error Handling, Models etc. 38 | 39 | ### Existing 40 | 41 | Using one of the Exising projects like: [moooofly/harbor-go-client](https://github.com/moooofly/harbor-go-client), [TimeBye/go-harbor](https://github.com/TimeBye/go-harbor) or [jiankunking/harbor-go-client](https://github.com/jiankunking/harbor-go-client) 42 | 43 | * 👍, because no painfull Api Code. 44 | * 👎, because a hard external dependency to the supported client methodes. 45 | * 👎, because it needs changes at all existing implementation for use the full harbor Api Sets. 46 | 47 | ### Swagger Based 48 | 49 | [Harbor Swagger](https://goharbor.io/docs/1.10/build-customize-contribute/configure-swagger/) 50 | 51 | * 👍, because no self written API Code. 52 | * 👍, because it support generate clients for different Harbor API Versions. 53 | * 👎, because depends to the SwaggerSpec Quality 54 | 55 | #### Swagger MissMatches 56 | 57 | * The Swagger missmatch will be fixed by a [json-patch](https://sookocheff.com/post/api/understanding-json-patch/). 58 | * As base for the Client we use the Harbor V2, most of the interface are useable for v1. 59 | 60 | 61 | 62 | ## Links 63 | 64 | * Github Harbor Swagger Issue [#12474](https://github.com/goharbor/harbor/issues/12474) 65 | * [evanphx/json-patch](https://github.com/evanphx/json-patch) 66 | * [json-patch](https://sookocheff.com/post/api/understanding-json-patch/) 67 | -------------------------------------------------------------------------------- /documentation/adr/index.md: -------------------------------------------------------------------------------- 1 | # Architectural Decision Log 2 | 3 | This log lists the architectural decisions for [nolte/terraform-provider-harbor](https://github.com/nolte/terraform-provider-harbor). 4 | 5 | 6 | 7 | - [ADR-0000](../adr0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records 8 | - [ADR-0001](../adr0001-use-swagger-for-generate-http-client.md) - Need a GoBased ApiClient for Harbor 9 | 10 | 11 | 12 | For new ADRs, please use [template.md](template.md) as basis. 13 | More information on MADR is available at . 14 | General information about architectural decision records is available at . 15 | -------------------------------------------------------------------------------- /documentation/adr/template.md: -------------------------------------------------------------------------------- 1 | # [short title of solved problem and solution] 2 | 3 | * Status: [proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)] 4 | * Deciders: [list everyone involved in the decision] 5 | * Date: [YYYY-MM-DD when the decision was last updated] 6 | 7 | Technical Story: [description | ticket/issue URL] 8 | 9 | ## Context and Problem Statement 10 | 11 | [Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.] 12 | 13 | ## Decision Drivers 14 | 15 | * [driver 1, e.g., a force, facing concern, …] 16 | * [driver 2, e.g., a force, facing concern, …] 17 | * … 18 | 19 | ## Considered Options 20 | 21 | * [option 1] 22 | * [option 2] 23 | * [option 3] 24 | * … 25 | 26 | ## Decision Outcome 27 | 28 | Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | … | comes out best (see below)]. 29 | 30 | ### Positive Consequences 31 | 32 | * [e.g., improvement of quality attribute satisfaction, follow-up decisions required, …] 33 | * … 34 | 35 | ### Negative Consequences 36 | 37 | * [e.g., compromising quality attribute, follow-up decisions required, …] 38 | * … 39 | 40 | ## Pros and Cons of the Options 41 | 42 | ### [option 1] 43 | 44 | [example | description | pointer to more information | …] 45 | 46 | * Good, because [argument a] 47 | * Good, because [argument b] 48 | * Bad, because [argument c] 49 | * … 50 | 51 | ### [option 2] 52 | 53 | [example | description | pointer to more information | …] 54 | 55 | * Good, because [argument a] 56 | * Good, because [argument b] 57 | * Bad, because [argument c] 58 | * … 59 | 60 | ### [option 3] 61 | 62 | [example | description | pointer to more information | …] 63 | 64 | * Good, because [argument a] 65 | * Good, because [argument b] 66 | * Bad, because [argument c] 67 | * … 68 | 69 | ## Links 70 | 71 | * [Link type] [Link to ADR] 72 | * … 73 | -------------------------------------------------------------------------------- /documentation/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/magefile/mage v1.10.0 7 | github.com/otiai10/copy v1.2.0 8 | ) 9 | -------------------------------------------------------------------------------- /documentation/go.sum: -------------------------------------------------------------------------------- 1 | github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= 2 | github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 3 | github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= 4 | github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= 5 | github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= 6 | github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= 7 | github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= 8 | github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= 9 | github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= 10 | github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= 11 | -------------------------------------------------------------------------------- /documentation/mage.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/magefile/mage/mage" 9 | ) 10 | 11 | func main() { os.Exit(mage.Main()) } 12 | -------------------------------------------------------------------------------- /documentation/magefile.go: -------------------------------------------------------------------------------- 1 | //+build mage 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/magefile/mage/sh" 14 | copy "github.com/otiai10/copy" 15 | ) 16 | 17 | func GenerateDocumation() error { 18 | documentationTemplatesDir := "./provider_doc" 19 | outputDir := "../docs" 20 | mkdocsOut := "./mkdocs/gen" 21 | file, err := ioutil.TempDir("", "example") 22 | log.Printf("file %s", file) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | defer os.Remove(file) 28 | err = filepath.Walk(documentationTemplatesDir, 29 | func(path string, info os.FileInfo, err error) error { 30 | if err != nil { 31 | return err 32 | } 33 | log.Print(path) 34 | if strings.HasSuffix(path, ".mdpp") { 35 | fmt.Println(path, info.Size()) 36 | targetPath := strings.ReplaceAll(filepath.Join(file, path), ".mdpp", ".md") 37 | command := fmt.Sprintf("%s -o %s", path, targetPath) 38 | os.MkdirAll(filepath.Dir(targetPath), 0700) 39 | //log.Printf("Command %s", filepath.Dir(targetPath)) 40 | err = sh.Run("markdown-pp", strings.Split(command, " ")...) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | return nil 46 | }) 47 | if err != nil { 48 | log.Println(err) 49 | } 50 | copyCommand := fmt.Sprintf("%s/provider_doc", file) 51 | 52 | os.RemoveAll(outputDir) 53 | os.RemoveAll(mkdocsOut) 54 | os.MkdirAll(filepath.Dir(outputDir), 0700) 55 | os.MkdirAll(filepath.Dir(mkdocsOut), 0700) 56 | err = copy.Copy(copyCommand, outputDir) 57 | err = copy.Copy(copyCommand, mkdocsOut) 58 | 59 | fmt.Printf("COPY: %s", copyCommand) 60 | return err 61 | } 62 | -------------------------------------------------------------------------------- /documentation/mkdocs/css/extras.css: -------------------------------------------------------------------------------- 1 | .task-list-control { 2 | display: inline !important; 3 | } -------------------------------------------------------------------------------- /documentation/mkdocs/css/table.css: -------------------------------------------------------------------------------- 1 | .wy-nav-content { 2 | background: white; 3 | max-width: 100%; 4 | height: 100vh; 5 | margin: 0; 6 | } 7 | -------------------------------------------------------------------------------- /documentation/mkdocs/guides/kind.md: -------------------------------------------------------------------------------- 1 | # E2E Tests With Kind 2 | 3 | For Quick and Easy Local Development it is Recommended to use a Vanilla [Harbor]() installation. The Local Test environment will be used the [Helm Chart](https://helm.sh/) from [goharbor/harbor-helm](https://github.com/goharbor/harbor-helm/releases). 4 | 5 | All relevant `Makefile` goals are prefixed with ```e2s_*```. 6 | 7 | | Goal | Description | 8 | |-------------------------|----------------------------------------------------------------------------------------------| 9 | | `e2e_prepare` | Configure the Kind based Env, and install the latest version from the Harbor Chart. | 10 | | `e2e_prepare_harbor_v1` | Install a Harbor v1 to the Kind Cluster | 11 | | `e2e_prepare_harbor_v2` | Install a Harbor v2 to the Kind Cluster | 12 | | `e2e_test_v1` | Starting the go based tests again a Harbor V1 Deployment | 13 | | `e2e_test_v2` | Starting the go based tests again a Harbor V2 Deployment | 14 | | `e2e_test_classic` | Test the Terraform Scripts from `examples` with [terratest](https://terratest.gruntwork.io/) | 15 | | `e2e_clean_harbor` | Delete the Harbor helm chart from kind cluster | 16 | | `e2e_clean_cluster` | Remove the current kind cluster. | 17 | 18 | The same flavore of tests will be integrated into the [CI/CD](/guides/development/#cicd) Process. 19 | 20 | 21 | **Befor you execute `e2e_prepare` ensure that the `INGRESS_DOMAIN` to your local host** (By default we use the Docker Bridge Network Interface like `172-17-0-1.sslip.io`.) 22 | 23 | *example without change any files* 24 | 25 | ```bash 26 | cd ./tools && go run mage.go -v kind:recreate 27 | ./scripts/tst-01-prepare-harbor.sh "10-42-0-100.sslip.io" "1.2.0" 28 | ``` 29 | 30 | As wildcard DNS we use a service like [sslip.io](https://sslip.io/). 31 | The scripts will be starting the [Kind](https://kind.sigs.k8s.io) Cluster with [Ingress Support](https://kind.sigs.k8s.io/docs/user/ingress/), as Ingress Controller we use [Nginx](https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx) . 32 | 33 | The [Kind](https://kind.sigs.k8s.io) installation is part of the used [Visual Studio Code Devcontainer](/guides/development/#visual-studio-code-devcontainer) 34 | 35 | ## Go Based Tests 36 | 37 | For quick response and tests near the Code we use [Terrafomr Go Acceptance Tests](https://www.terraform.io/docs/extend/testing/acceptance-tests/index.html). 38 | 39 | ```bash 40 | make e2e_test 41 | ``` 42 | 43 | Tests will be matchs by the file name prefix `*_test.go`. 44 | 45 | ## Terratest File Based Tests 46 | 47 | The Classic Terraform File based tests are located at `examples/**`, and executed with [terratest](https://terratest.gruntwork.io/). For execution you need, a full runnable Terraform Enviroment with the current version from the harbor terraform provider. 48 | 49 | ```bash 50 | # compile the provider 51 | make compile 52 | 53 | # copy provider to local ~/.terraform.d/plugins/linux_amd64 folder 54 | make install 55 | 56 | # start the tests 57 | make e2e_test_classic 58 | ``` 59 | -------------------------------------------------------------------------------- /documentation/mkdocs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "harbor" 3 | page_title: "Provider: Harbor" 4 | sidebar_current: "docs-harbor-index" 5 | description: |- 6 | The Harbor Registry provider is used to interact with the many resources supported by Harbor. The provider needs to be configured with the proper credentials before it can be used. 7 | --- 8 | 9 | # Harbor Provider 10 | 11 | Summary of what the provider is for, including use cases and links to 12 | app/service documentation. 13 | 14 | 15 | ## Example Usage 16 | 17 | ```hcl 18 | # example for harbor v2 api usage 19 | provider "harborv2" { 20 | host = "demo.goharbor.io" 21 | schema = "https" 22 | insecure = true 23 | basepath = "/api/v2" 24 | username = "admin" 25 | password = "Harbor12345" 26 | } 27 | 28 | # example for harbor v1 api usage 29 | provider "harborv1" { 30 | host = var.harbor_endpoint 31 | schema = "https" 32 | insecure = true 33 | basepath = var.harbor_base_path 34 | username = "admin" 35 | password = "Harbor12345" 36 | } 37 | ``` 38 | 39 | ## Argument Reference 40 | 41 | In addition to [generic `provider` arguments](https://www.terraform.io/docs/configuration/providers.html) 42 | (e.g. `alias` and `version`), the following arguments are supported in the Harbor 43 | `provider` block: 44 | 45 | * `host` - (Required) Hostname from the [Harbor](https://goharbor.io) Service. like _demo.goharbor.io_, can also be specified with the `HARBOR_ENDPOINT` environment variable. 46 | 47 | * `username` - (Required) Username for authorize at the harbor, can also be specified with the `HARBOR_USERNAME` environment variable. 48 | 49 | * `password` - (Required) Password from given user, can also be specified with the `HARBOR_PASSWORD` environment variable. 50 | 51 | * `schema` - (Optional) Set Used http Schema, possible values are: ```https,http```. Default: ```https``` 52 | 53 | * `insecure` - (Optional) Verify Https Certificates. Default: ```false```, can also be specified with the `HARBOR_INSECURE` environment variable. 54 | 55 | * `basepath` - (Optional) The Harbor Api basepath, for example use ```/api``` for default HarborV1 and ```/api/v2.0``` for Harbor V2 Deployments. Default: ```/api```, can also be specified with the `HARBOR_BASEPATH` environment variable. 56 | 57 | 58 | ## Install the Custom Provider 59 | 60 | Download the Latest Release, [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nolte/terraform-provider-harbor)](https://github.com/nolte/terraform-provider-harbor/releases). 61 | 62 | ```bash 63 | # for example https://github.com/nolte/terraform-provider-harbor/releases/download/release/v0.1.0/terraform-provider-harbor_v0.1.0_linux_amd64.tar.gz 64 | LATEST_LINUX_RELEASE=$(curl -sL https://api.github.com/repos/nolte/terraform-provider-harbor/releases/latest | jq -r '.assets[].browser_download_url' | grep '_linux_amd64') 65 | TAG_NAME=$(curl -sL https://api.github.com/repos/nolte/terraform-provider-harbor/releases/latest | jq -r '.tag_name') 66 | 67 | # direct install to your personal plugin directory 68 | wget -q $LATEST_LINUX_RELEASE -o /tmp/terraform-provider-harbor.zip \ 69 | && unzip -j "/tmp/terraform-provider-harbor.zip" "terraform-provider-harbor_${TAG_NAME}" -d /home/${USERNAME}/.terraform.d/plugins/linux_amd64 \ 70 | && rm /tmp/terraform-provider-harbor.zip 71 | ``` 72 | 73 | Here a link to the Terraform Doc how to install [third-party-plugins](https://www.terraform.io/docs/configuration/providers.html#third-party-plugins) 74 | -------------------------------------------------------------------------------- /documentation/mkdocs/js/table.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function() { 2 | document.querySelectorAll("table").forEach(function(table) { 3 | table.classList.add("docutils"); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /documentation/provider_doc/data-sources/harbor_label.mdpp: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_label 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test-part-2/data_label.tf" 7 | 8 | data "harbor_label" "label_2" { 9 | id = 4 10 | } 11 | 12 | ``` 13 | 14 | ## Argument Reference 15 | 16 | - `id` - (Optional, int) ID of the label. 17 | - `name` - (Optional, string) Name of the label. 18 | - `scope` - (Optional, string) Scope of the label. 19 | 20 | ## Attributes Reference 21 | 22 | - `id` - (int) Unique ID of the label. 23 | - `name` - (string) Name of the label. 24 | - `description` - (Optional) The description of the label account will be displayed in harbor. 25 | - `color` - (Optional) The colour the label. 26 | - `scope` - (Optional) The scope the label, `p` for project and `g` for global. 27 | - `project_id` - (Optional) The ID of project that the label belongs to. 28 | -------------------------------------------------------------------------------- /documentation/provider_doc/data-sources/harbor_project.mdpp: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_project 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test-part-2/data_project.tf" 7 | 8 | data "harbor_project" "project_2" { 9 | id = 4 10 | } 11 | 12 | ``` 13 | 14 | ## Argument Reference 15 | 16 | - `id` - (Optional, int) ID of the project. 17 | - `name` - (Optional, string) Name of the project. 18 | 19 | ## Attributes Reference 20 | 21 | - `id` - (int) Unique ID of the project. 22 | - `name` - (string) Name of the project. 23 | -------------------------------------------------------------------------------- /documentation/provider_doc/data-sources/harbor_registry.mdpp: -------------------------------------------------------------------------------- 1 | # Data Source: harbor_registry 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test-part-2/data_registry.tf" 7 | 8 | data "harbor_registry" "registry_2" { 9 | id = 4 10 | } 11 | 12 | ``` 13 | 14 | ## Argument Reference 15 | 16 | - `id` - (Optional, string) ID of the registry. 17 | - `name` - (Optional, string) Name of the registry. 18 | 19 | ## Attributes Reference 20 | 21 | - `id` - (int) Unique ID of the registry. 22 | - `name` - (string) Name of the registry. 23 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_config_system.mdpp: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "harbor" 3 | #subcategory: "config" 4 | page_title: "Harbor: harbor_config_system" 5 | description: |- 6 | Manages an Base Harbor System Configuration 7 | --- 8 | 9 | # Resource: harbor_config_system 10 | 11 | ## Example Usage 12 | 13 | ```hcl 14 | resource "harbor_config_system" "main" { 15 | project_creation_restriction = "adminonly" 16 | robot_token_expiration = 5259492 17 | } 18 | ``` 19 | 20 | ## Argument Reference 21 | The following arguments are supported: 22 | 23 | * **project_creation_restriction** - (Optional) Who can create projects within Harbor. Can be **"adminonly"** or **"everyone"** 24 | 25 | * **robot_token_expiration** - (Optional) The amount of time in minutes a robot account will expiry. 26 | 27 | `NOTE: If the time is set to high you will get a 500 internal server error message when creating robot accounts` 28 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_configuration.mdpp: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_config_auth" 4 | description: |- 5 | Manages an Base Harbor Auth Configuration 6 | --- 7 | 8 | 9 | # Resource: harbor_config_auth 10 | 11 | ## Example Usage 12 | 13 | ```hcl 14 | resource "harbor_config_auth" "oidc" { 15 | auth_mode = "oidc_auth" 16 | oidc_name = "azure" 17 | oidc_endpoint = "https://login.microsoftonline.com/{GUID goes here}/v2.0" 18 | oidc_client_id = "OIDC Client ID goes here" 19 | oidc_client_secret = "ODDC Client Secret goes here" 20 | oidc_scope = "openid,email" 21 | oidc_verify_cert = true 22 | } 23 | ``` 24 | 25 | ## Argument Reference 26 | The following arguments are supported: 27 | 28 | * **auth_mode** - (Required) Harbor authentication mode. Can be **"oidc_auth"** or **"db_auth"**. (Default: **"db_auth"**) 29 | 30 | * **oidc_name** - (Optional) The name of the oidc provider name. (Required - if auth_mode set to **oidc_auth**) 31 | 32 | * **oidc_endpoint** - (Optional) The URL of an OIDC-complaint server. (Required - if auth_mode set to **oidc_auth**) 33 | 34 | * **oidc_client_id** - (Optional) The client id for the oidc server. (Required - if auth_mode set to **oidc_auth**) 35 | 36 | * **oidc_client_serect** - (Optional) The client secret for the oidc server. (Required - if auth_mode set to **oidc_auth**) 37 | 38 | * **oidc_groups_claim** - (Optional) The name of the claim in the token whose values is the list of group names. 39 | 40 | `NOTE: "oidc_groups_claim" can only be used with harbor version v1.10.1 and above` 41 | 42 | * **oidc_scope** - (Optional) The scope sent to OIDC server during authentication. It has to contain “openid”. (Required - if auth_mode set to **oidc_auth**) 43 | 44 | * **oidc_verify_cert** - (Optional) Set to **"false"** if your OIDC server is using a self-signed certificate. (Required - if auth_mode set to **oidc_auth**) 45 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_label.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_label 2 | 3 | Harbor Doc: [Managing Labels](https://goharbor.io/docs/1.10/working-with-projects/working-with-images/create-labels/) 4 | Harbor Api: [/labels](https://demo.goharbor.io/#/Products/post_labels) 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | !INCLUDE "../examples/tf-acception-test/label.tf" 10 | 11 | !INCLUDE "../examples/tf-acception-test-part-2/data_project.tf" 12 | 13 | !INCLUDE "../examples/tf-acception-test-part-2/label.tf" 14 | ``` 15 | 16 | ## Argument Reference 17 | 18 | The following arguments are required: 19 | 20 | * `name` - (Required) Name of the Project. 21 | 22 | The following arguments are optional: 23 | 24 | * `description` - (Optional) The description of the label account will be displayed in harbor. 25 | 26 | * `color` - (Optional) The colour the label. 27 | 28 | * `scope` - (Optional) The scope the label, `p` for project and `g` for global. 29 | 30 | * `project_id` - (Optional) The ID of project that the label belongs to, must be set if scope project. 31 | 32 | ## Attributes Reference 33 | 34 | In addition to all argument, the following attributes are exported: 35 | 36 | * `id` - The id of the registry with harbor. 37 | 38 | ## Import 39 | 40 | Harbor Projects can be imported using the `harbor_label`, e.g. 41 | 42 | ```sh 43 | terraform import harbor_label.helmhub 1 44 | ``` 45 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_project.mdpp: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_project" 4 | description: |- 5 | Manages an Harbor Project 6 | --- 7 | 8 | # Resource: harbor_project 9 | 10 | Handle a [Harbor Project Resource](https://goharbor.io/docs/1.10/working-with-projects/create-projects/). 11 | 12 | ## Example Usage 13 | 14 | ```hcl 15 | !INCLUDE "../examples/tf-acception-test/project.tf" 16 | ``` 17 | 18 | ## Argument Reference 19 | 20 | The following arguments are required: 21 | 22 | * `name` - (Required) Name of the Project. 23 | 24 | The following arguments are optional: 25 | 26 | * `public` - (Optional) Handle the access to the hosted images. Default: `true` 27 | 28 | If `true` Any user can pull images from this project. This is a convenient way for you to share repositories with others. 29 | 30 | If `false` Only users who are members of the project can pull images 31 | 32 | * `vulnerability_scanning` - (Optional) Activate [Vulnerability Scanning](https://goharbor.io/docs/1.10/administration/vulnerability-scanning/). Default: `true` 33 | 34 | * `reuse_sys_cve_whitelist` - (Optional) Whether this project should reuse the system level CVE whitelist as the whitelist of its own. Default: `true` 35 | 36 | If `true` The whitelist associated with this project will be ignored. 37 | 38 | If `false` The project will use the whitelist defined by `cve_whitelist`. 39 | 40 | * `cve_whitelist` - (Optional) List of whitelisted CVE ids for the project. 41 | 42 | ## Attributes Reference 43 | 44 | In addition to all arguments above, the following attributes are exported: 45 | 46 | * `id` - Harbor Project ID. 47 | 48 | ## Import 49 | 50 | Harbor Projects can be imported using the `harbor_project`, e.g. 51 | 52 | ``` 53 | $ terraform import harbor_project.main 1 54 | ``` 55 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_project_member.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_project_member 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test/project.tf" 7 | 8 | !INCLUDE "../examples/tf-acception-test/usergroup.tf" 9 | 10 | !INCLUDE "../examples/tf-acception-test/project_member.tf" 11 | ``` 12 | 13 | ## Argument Reference 14 | 15 | The following arguments are supported: 16 | 17 | * `role` - (Required) The role permission to be given to the group: `project_admin`, `master`, `developer`, `guest` or `limited_guest`. 18 | 19 | * `group_type` - (Required) The group type: `ldap`, `http` or `oidc`. 20 | 21 | * `project_id` - (Required) The project id of the project to be given permission to the group members. 22 | 23 | * `group_name` - (Required) The name of the user group to be given permissions for the project. 24 | 25 | ## Attributes Reference 26 | 27 | In addition to all arguments, the following attribute is exported: 28 | 29 | * `id` - The id of the project account: format is `${project_id}/${group_id}`. 30 | 31 | ## Import 32 | 33 | Harbor Project member can be imported using the `harbor_project_member`, e.g. 34 | 35 | ```sh 36 | terraform import harbor_project_member.developers_main 12/5 37 | ``` 38 | 39 | ## Known limitations 40 | 41 | The provider currently only handles group membership, not user. 42 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_registry.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_registry 2 | 3 | Harbor Doc: [managing-registries](https://goharbor.io/docs/2.0.0/administration/configuring-replication/create-replication-endpoints/#managing-registries) 4 | Harbor Api: [Create](https://demo.goharbor.io/#/Products/post_registries) 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | !INCLUDE "../examples/tf-acception-test/registry.tf" 10 | ``` 11 | 12 | ## Argument Reference 13 | 14 | The following arguments are supported: 15 | 16 | * `name` - (Required) of the project that will be created in harbor. 17 | 18 | * `url` - (Required) The registry remote endpoint, like `https://hub.docker.com`. 19 | 20 | * `type` - (Required) registry Type possible values are `huawei-SWR, aws-ecr, ali-acr, jfrog-artifactory, gitlab, docker-registry, docker-hub, azure-acr, quay-io, helm-hub, harbor, google-gcr`. 21 | 22 | * `description` - (Optional) The description of the registry will be displayed in harbor. 23 | 24 | * `insecure` - (Optional) Harbor ignores insecure external registry errors. Can be set to `true` or `false` (Default: `false`) 25 | 26 | * `access_key` - (Optional) The registry access key. 27 | 28 | * `access_secret` - (Optional) The registry access secret. 29 | 30 | * `credential_type` - (Optional) Credential type, such as 'basic', 'oauth'. 31 | 32 | ## Attributes Reference 33 | 34 | In addition to all argument, the following attributes are exported: 35 | 36 | * `id` - The id of the registry with harbor. 37 | 38 | ## Import 39 | 40 | Harbor Projects can be imported using the `harbor_registry`, e.g. 41 | 42 | ```sh 43 | terraform import harbor_registry.helmhub 1 44 | ``` 45 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_replication.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_replication 2 | 3 | Harbor Doc: [configuring-replication](https://goharbor.io/docs/2.0.0/administration/configuring-replication/) 4 | 5 | 6 | ## Example Usage 7 | 8 | ```hcl 9 | !INCLUDE "../examples/tf-acception-test/project.tf" 10 | 11 | !INCLUDE "../examples/tf-acception-test/registry.tf" 12 | 13 | !INCLUDE "../examples/tf-acception-test/replication.tf" 14 | ``` 15 | 16 | ## Argument Reference 17 | 18 | The following arguments are supported: 19 | 20 | * `name` - (Required) of the replication that will be created in harbor. 21 | 22 | * `description` - (Optional) of replication that will be displayed in harbor. 23 | 24 | * `source_registry_id` - (Optional) Used for pull the resources from the remote registry to the local Harbor. 25 | 26 | * `source_registry_filter_name` - (Optional) Filter the name of the resource. Leave empty or use '\*\*' to match all. 'library/\*\*' only matches resources under 'library'. 27 | 28 | * `source_registry_filter_tag` - (Optional) Filter the tag/version part of the resources. Leave empty or use '\*\*' to match all. '1.0*' only matches the tags that starts with '1.0'. 29 | 30 | * `destination_namespace` - (Optional) Destination namespace Specify the destination namespace. If empty, the resources will be put under the same namespace as the source. 31 | 32 | * `destination_registry_id` - (Optional) The target Registry ID, used only for `push-based` replications. 33 | 34 | * `trigger_mode` - (Optional) Can be `manual`,`scheduled` and for push-based addition `event_based`, Default: `manual` 35 | 36 | * `trigger_cron` - (Optional) Used cron for `scheduled` trigger mode, like `* * 5 * * *` 37 | 38 | * `override` - (Optional) Specify whether to override the resources at the destination if a resource with the same name exists. Default: `false` 39 | 40 | * `enabled` - (Optional) 41 | 42 | 43 | ## Attributes Reference 44 | 45 | In addition to all argument, the following attributes are exported: 46 | 47 | * `id` - The id of the registry policy with harbor. 48 | 49 | ## Import 50 | 51 | Harbor Projects can be imported using the `harbor_replication`, e.g. 52 | 53 | ```sh 54 | terraform import harbor_replication.helmhub_prometheus 1 55 | ``` 56 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_retention_policy.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_retention_policy 2 | 3 | Harbor official documentation feature reference: https://goharbor.io/docs/2.0.0/working-with-projects/working-with-images/create-tag-retention-rules/ 4 | 5 | ## Example Usage 6 | 7 | ```hcl 8 | !INCLUDE "../examples/tf-acception-test/project.tf" 9 | 10 | !INCLUDE "../examples/tf-acception-test/retention_policy.tf" 11 | ``` 12 | 13 | ## Argument Reference 14 | 15 | The following arguments are supported: 16 | 17 | * `algorithm` - (Optional) Algorithm used to aggregate rules. Defaults to `"or"`. 18 | 19 | * `scope` - (Required) Describes the (project) scope of this retention policy. 20 | 21 | * `rule` - (Required) List of retention rules that compose the policy. 22 | 23 | * `trigger` - (Required) Defines a schedule with a cron rule. 24 | 25 | ## Nested Blocks 26 | 27 | ### scope 28 | 29 | #### Arguments 30 | 31 | * `level` - (Optional) Defines the level the scope applies to. Defaults to `"project"`. 32 | 33 | * `ref` - (Required) Reference id (usually project id) the retention policy refers to. 34 | 35 | ### rule 36 | 37 | #### Arguments 38 | 39 | * `disabled` - (Optional) Specifies if the rule should be taken in account or not (ie disabled). Defaults to `false`. 40 | 41 | * `action` - (Optional) Specifies the action to do if set of rules is matched. Defaults to `"retain"`. 42 | 43 | * `template` - (Required) kind of rules among the following: 44 | 45 | * `latestPushedK`: (retain) the most recently pushed # artifacts 46 | 47 | * `latestPulledN`: (retain) the most recently pulled # artifacts 48 | 49 | * `nDaysSinceLastPush`: (retain) the artifacts pushed with the last # days 50 | 51 | * `nDaysSinceLastPull`: (retain) the artifacts pulled with the last # days 52 | 53 | * `always`: (retain) always 54 | 55 | * `params` - (Optional) Map of params for the kind of rule (number of days or artifacts associated to the rule template). Defaults to empty map. 56 | 57 | Note that multiple key-values can be specified instead of only the one matching the template. The other ones would be disabled but recorded as default choice to the associated template within the UI combo-box. 58 | 59 | * `tag_selectors` - (Required) Describes the image tags on which to apply the rule. 60 | 61 | * `scope_selectors` - (Required) Describes the image name(s) on which to apply the rule. 62 | 63 | #### Attributes 64 | 65 | * `id` - The id of the rule (currently not used and set to `0` whatever the number of rules). 66 | 67 | * `priority` - A priority tag to know in which order the set of rules should b performed (currently not used and set to `0`). 68 | 69 | ### `tag_selectors` 70 | 71 | #### Arguments 72 | 73 | * `kind` - (Optional) The kind of tag selector. Defaults to `doublestar`. 74 | 75 | * `extras` - (Optional) Describes extra info for tag selector as a json encoded string, supports mainly the following key value: `untagged: (false|true)` to match or not the untagged artifacts in the rule. Defaults to `""`. 76 | 77 | * `decoration` - (Required) Specifies if the pattern should be matched (`"matches"`) or excluded (`"excludes"`). 78 | 79 | * `pattern` - (Required) Describes the pattern that should match (or exclude) image tags 80 | 81 | ### `scope_selectors` 82 | 83 | #### Arguments 84 | 85 | * `kind` - (Optional) The kind of scope selector. Defaults to `doublestar`. 86 | 87 | * `extras` - (Optional) Describes extra info for scope selector as a json encoded string, does not support any known key value as now. Defaults to `""`. 88 | 89 | * `decoration` - (Required) Specifies if the pattern should be matched (`"repoMatches"`) or excluded (`"repoExcludes"`). 90 | 91 | * `pattern` - (Required) Describes the pattern that should match (or exclude) image names 92 | 93 | ### `trigger` 94 | 95 | #### Arguments 96 | 97 | * `kind` - (Optional) The kind of trigger. Defaults to `Schedule`. 98 | 99 | * `settings` - (Required) The trigger settings. 100 | 101 | #### Attributes 102 | 103 | * `references` - Reference to an internal job responsible for applying the trigger. 104 | 105 | ### `settings` 106 | 107 | #### Arguments 108 | 109 | * `cron` - (Optional) 6-fields cron pattern for triggering the retention policy execution by cron schedule. Defaults to `""` (which refers to a "None" Schedule). 110 | 111 | ### `references` 112 | 113 | #### Attributes 114 | 115 | * `job_id` - Reference to the internal job id associated to the trigger. 116 | 117 | ## Attributes Reference 118 | 119 | In addition to all arguments, the following attribute is exported: 120 | 121 | * `id` - The id of the retention policy. 122 | 123 | ## Import 124 | 125 | Harbor Retention policies can be imported using the `harbor_retention_policy`, e.g. 126 | 127 | ```sh 128 | terraform import harbor_retention_policy.cleanup 2 129 | ``` 130 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_robot_account.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_robot_account 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test/project.tf" 7 | 8 | !INCLUDE "../examples/tf-acception-test/robot_account.tf" 9 | ``` 10 | 11 | ## Argument Reference 12 | 13 | The following arguments are supported: 14 | 15 | * `name` - (Required) The of the project that will be created in harbor. 16 | 17 | * `description` - (Optional) The description of the robot account will be displayed in harbor. 18 | 19 | * `project_id` - (Required) The project id of the project that the robot account will be associated with. 20 | 21 | * `actions` - (Optional) 22 | 23 | ## Attributes Reference 24 | 25 | In addition to all argument, the following attributes are exported: 26 | 27 | * `id` - The id of the robot account. 28 | 29 | * `token` - The token of the robot account. 30 | 31 | ## Import 32 | 33 | Harbor Projects can be imported using the `harbor_robot_account`, e.g. 34 | 35 | ```sh 36 | terraform import harbor_robot_account.master_robot 29 37 | ``` 38 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_tasks.mdpp: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "Resources" 3 | page_title: "Harbor: harbor_tasks" 4 | description: |- 5 | Manages an Harbor Task 6 | --- 7 | 8 | # Resource: harbor_tasks 9 | 10 | ## Example Usage 11 | 12 | ```hcl 13 | !INCLUDE "../examples/tf-acception-test/tasks.tf" 14 | ``` 15 | 16 | ## Argument Reference 17 | The following arguments are supported: 18 | 19 | * **vulnerability_scan_policy** - (Optional) The frequency of the vulnerability scanning is done. Can be to **"hourly"**, **"daily"** or **"weekly"** 20 | -------------------------------------------------------------------------------- /documentation/provider_doc/resources/harbor_usergroup.mdpp: -------------------------------------------------------------------------------- 1 | # Resource: harbor_usergroup 2 | 3 | ## Example Usage 4 | 5 | ```hcl 6 | !INCLUDE "../examples/tf-acception-test/usergroup.tf" 7 | ``` 8 | 9 | 10 | ## Argument Reference 11 | 12 | The following arguments are supported: 13 | 14 | * `name` - (Required) The name of the user group to be created. 15 | 16 | * `ldap_dn` - (Optional) The LDAP Group distingish name if `type` is `ldap`, defaults to `""`. 17 | 18 | * `type` - (Required) The group type: `ldap`, `http` or `oidc`. 19 | 20 | ## Attributes Reference 21 | 22 | In addition to all arguments, the following attribute is exported: 23 | 24 | * `id` - The id of the group. 25 | 26 | ## Import 27 | 28 | Harbor Usergroups can be imported using the `harbor_usergroup`, e.g. 29 | 30 | ```sh 31 | terraform import harbor_usergroup.developers 5 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/test/terraform_harbor_integration_test.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | package test 4 | 5 | import ( 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/gruntwork-io/terratest/modules/terraform" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func testsBaseDirectory() string { 14 | return "../" 15 | } 16 | 17 | func TestHarborBaseComponentsExists(t *testing.T) { 18 | 19 | terraformOptions := &terraform.Options{ 20 | TerraformDir: filepath.Join(testsBaseDirectory(), "tf-acception-test"), 21 | //Parallelism: 1, 22 | //MaxRetries: 5, 23 | //TimeBetweenRetries: time.Second + 10, 24 | } 25 | terraformOptionsPartTwo := &terraform.Options{ 26 | TerraformDir: filepath.Join(testsBaseDirectory(), "tf-acception-test-part-2"), 27 | //Parallelism: 1, 28 | //MaxRetries: 5, 29 | //TimeBetweenRetries: time.Second + 10, 30 | } 31 | 32 | defer terraform.Destroy(t, terraformOptions) 33 | defer terraform.Destroy(t, terraformOptionsPartTwo) 34 | 35 | terraform.InitAndApply(t, terraformOptions) 36 | 37 | terraform.InitAndApply(t, terraformOptionsPartTwo) 38 | 39 | output := terraform.Output(t, terraformOptions, "harbor_project_id") 40 | assert.NotNil(t, output) 41 | 42 | } 43 | 44 | func TestHarborSystemConfig(t *testing.T) { 45 | 46 | terraformOptions := &terraform.Options{ 47 | TerraformDir: filepath.Join(testsBaseDirectory(), "tf-project-only"), 48 | //Parallelism: 1, 49 | //MaxRetries: 5, 50 | } 51 | 52 | defer terraform.Destroy(t, terraformOptions) 53 | 54 | terraform.InitAndApply(t, terraformOptions) 55 | } 56 | -------------------------------------------------------------------------------- /examples/tf-acception-test-part-2/data_label.tf: -------------------------------------------------------------------------------- 1 | data "harbor_label" "label_1" { 2 | name = "testlabel-acc-classic" 3 | scope = "g" 4 | } 5 | -------------------------------------------------------------------------------- /examples/tf-acception-test-part-2/data_project.tf: -------------------------------------------------------------------------------- 1 | data "harbor_project" "project_1" { 2 | name = "main" 3 | } 4 | -------------------------------------------------------------------------------- /examples/tf-acception-test-part-2/data_registry.tf: -------------------------------------------------------------------------------- 1 | data "harbor_registry" "registry_1" { 2 | name = "dockerhub-acc-classic" 3 | } 4 | -------------------------------------------------------------------------------- /examples/tf-acception-test-part-2/label.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_label" "project_label" { 2 | name = "projectlabel-acc-classic" 3 | description = "Test Label for Project" 4 | color = "#333333" 5 | scope = "p" 6 | project_id = data.harbor_project.project_1.id 7 | } 8 | -------------------------------------------------------------------------------- /examples/tf-acception-test-part-2/provider.tf: -------------------------------------------------------------------------------- 1 | # will be configure by env vars from script 2 | provider "harbor" { 3 | 4 | } 5 | 6 | terraform { 7 | required_providers { 8 | harbor = { 9 | source = "registry.terraform.private/nolte/harbor" 10 | version = "~> 0.0.1" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/tf-acception-test/label.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_label" "main" { 2 | name = "testlabel-acc-classic" 3 | description = "Test Label" 4 | color = "#61717D" 5 | scope = "g" 6 | } 7 | -------------------------------------------------------------------------------- /examples/tf-acception-test/outputs.tf: -------------------------------------------------------------------------------- 1 | output "harbor_project_id" { 2 | value = harbor_project.main.id 3 | } 4 | -------------------------------------------------------------------------------- /examples/tf-acception-test/project.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "main" 3 | public = false # (Optional) Default value is false 4 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 5 | reuse_sys_cve_whitelist = false # (Optional) Default value is true. 6 | cve_whitelist = ["CVE-2020-12345", "CVE-2020-54321"] 7 | } 8 | -------------------------------------------------------------------------------- /examples/tf-acception-test/project_member.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project_member" "developers_main" { 2 | project_id = harbor_project.main.id 3 | role = "guest" 4 | group_type = "http" 5 | group_name = harbor_usergroup.developers.name 6 | } 7 | -------------------------------------------------------------------------------- /examples/tf-acception-test/provider.tf: -------------------------------------------------------------------------------- 1 | # will be configure by env vars from script 2 | provider "harbor" { 3 | 4 | } 5 | 6 | terraform { 7 | required_providers { 8 | harbor = { 9 | source = "registry.terraform.private/nolte/harbor" 10 | version = "~> 0.0.1" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/tf-acception-test/registry.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_registry" "dockerhub" { 2 | name = "dockerhub-acc-classic" 3 | url = "https://hub.docker.com" 4 | type = "docker-hub" 5 | description = "Docker Hub Registry" 6 | insecure = false 7 | } 8 | 9 | resource "harbor_registry" "helmhub" { 10 | name = "helmhub-acc-classic" 11 | url = "https://hub.helm.sh" 12 | type = "helm-hub" 13 | description = "Helm Hub Registry" 14 | insecure = false 15 | } 16 | -------------------------------------------------------------------------------- /examples/tf-acception-test/replication.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_replication" "pull_helm_chart" { 2 | name = "helm-prometheus-operator-acc-classic" 3 | description = "Prometheus Operator Replica ACC Classic" 4 | source_registry_id = harbor_registry.helmhub.id 5 | source_registry_filter_name = "stable/prometheus-operator" 6 | source_registry_filter_tag = "**" 7 | destination_namespace = harbor_project.main.name 8 | } 9 | 10 | resource "harbor_replication" "push_helm_chart" { 11 | name = "docker-push-acc-classic" 12 | description = "Push Docker Replica ACC Classic" 13 | destination_registry_id = harbor_registry.dockerhub.id 14 | source_registry_filter_name = "${harbor_project.main.name}/vscode-devcontainers/k8s-operator" 15 | source_registry_filter_tag = "**" 16 | destination_namespace = "notexisting" 17 | } 18 | -------------------------------------------------------------------------------- /examples/tf-acception-test/retention_policy.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_retention_policy" "cleanup" { 2 | scope { 3 | ref = harbor_project.main.id 4 | } 5 | 6 | rule { 7 | template = "always" 8 | tag_selectors { 9 | decoration = "matches" 10 | pattern = "master" 11 | extras = jsonencode({ 12 | untagged: false 13 | }) 14 | } 15 | scope_selectors { 16 | repository { 17 | kind = "doublestar" 18 | decoration = "repoMatches" 19 | pattern = "**" 20 | } 21 | } 22 | } 23 | 24 | rule { 25 | disabled = true 26 | template = "latestPulledN" 27 | params = { 28 | "latestPulledN" = 15 29 | "nDaysSinceLastPush" = 7 30 | } 31 | tag_selectors { 32 | kind = "doublestar" 33 | decoration = "excludes" 34 | pattern = "master" 35 | extras = jsonencode({ 36 | untagged: false 37 | }) 38 | } 39 | scope_selectors { 40 | repository { 41 | kind = "doublestar" 42 | decoration = "repoExcludes" 43 | pattern = "nginx-*" 44 | } 45 | } 46 | } 47 | 48 | trigger { 49 | settings { 50 | cron = "0 0 0 * * *" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/tf-acception-test/robot_account.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_robot_account" "master_robot" { 2 | name = "god" 3 | description = "Robot account used to push images to harbor" 4 | project_id = harbor_project.main.id 5 | actions = ["docker_read", "docker_write", "helm_read", "helm_write"] 6 | } 7 | 8 | output "harbor_robot_account_token" { 9 | value = harbor_robot_account.master_robot.token 10 | } 11 | -------------------------------------------------------------------------------- /examples/tf-acception-test/tasks.tf: -------------------------------------------------------------------------------- 1 | #resource "harbor_tasks" "main" { 2 | # vulnerability_scan_policy = "daily" 3 | #} 4 | -------------------------------------------------------------------------------- /examples/tf-acception-test/usergroup.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_usergroup" "developers" { 2 | name = "developers" 3 | type = "http" 4 | } 5 | -------------------------------------------------------------------------------- /examples/tf-project-only/main.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_config_email" "conf_email" { 2 | email_host = "main2" 3 | email_port = 25 4 | email_username = "main2" 5 | email_password = "main2" 6 | email_from = "main2" 7 | email_ssl = false 8 | } 9 | 10 | resource "harbor_config_auth" "oidc" { 11 | auth_mode = "oidc_auth" 12 | oidc_name = "azure" 13 | oidc_endpoint = "https://login.microsoftonline.com/v2.0" 14 | oidc_client_id = "OIDC Client ID goes here" 15 | oidc_client_secret = "ODDC Client Secret goes here" 16 | oidc_scope = "openid,email" 17 | oidc_verify_cert = true 18 | } 19 | 20 | resource "harbor_config_system" "main" { 21 | project_creation_restriction = "everyone" 22 | robot_token_expiration = 5259492 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/tf-project-only/provider.tf: -------------------------------------------------------------------------------- 1 | # will be configure by env vars from script 2 | provider "harbor" { 3 | 4 | } 5 | 6 | terraform { 7 | required_providers { 8 | harbor = { 9 | source = "registry.terraform.private/nolte/harbor" 10 | version = "~> 0.0.1" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gen/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nolte/terraform-provider-harbor/82f613ddba804139240f1336916e99d023614ac4/gen/.keep -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nolte/terraform-provider-harbor 2 | 3 | go 1.13 4 | 5 | require ( 6 | cloud.google.com/go v0.63.0 // indirect 7 | github.com/agext/levenshtein v1.2.3 // indirect 8 | github.com/apparentlymart/go-cidr v1.1.0 // indirect 9 | github.com/aws/aws-sdk-go v1.34.0 // indirect 10 | github.com/go-openapi/errors v0.19.7 11 | github.com/go-openapi/runtime v0.19.22 12 | github.com/go-openapi/spec v0.19.9 // indirect 13 | github.com/go-openapi/strfmt v0.19.5 14 | github.com/go-openapi/swag v0.19.9 15 | github.com/go-openapi/validate v0.19.11 16 | github.com/gruntwork-io/terratest v0.30.7 17 | github.com/hashicorp/go-hclog v0.14.1 // indirect 18 | github.com/hashicorp/go-multierror v1.1.0 // indirect 19 | github.com/hashicorp/go-uuid v1.0.2 // indirect 20 | github.com/hashicorp/hcl/v2 v2.6.0 // indirect 21 | github.com/hashicorp/terraform-config-inspect v0.0.0-20200806211835-c481b8bfa41e // indirect 22 | github.com/hashicorp/terraform-plugin-sdk v1.16.0 23 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect 24 | github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect 25 | github.com/mailru/easyjson v0.7.2 // indirect 26 | github.com/mattn/go-colorable v0.1.7 // indirect 27 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 28 | github.com/mitchellh/mapstructure v1.3.3 // indirect 29 | github.com/oklog/run v1.1.0 // indirect 30 | github.com/posener/complete v1.2.3 // indirect 31 | github.com/spf13/afero v1.3.3 // indirect 32 | github.com/stretchr/testify v1.6.1 33 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 34 | github.com/zclconf/go-cty v1.5.1 // indirect 35 | github.com/zclconf/go-cty-yaml v1.0.2 // indirect 36 | go.mongodb.org/mongo-driver v1.4.0 // indirect 37 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect 38 | golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 // indirect 39 | golang.org/x/tools v0.0.0-20200808145551-b8f4a4e40dc2 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /harbor/data_source_label.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 8 | ) 9 | 10 | func dataSourceLabel() *schema.Resource { 11 | return &schema.Resource{ 12 | Schema: map[string]*schema.Schema{ 13 | "name": { 14 | Type: schema.TypeString, 15 | Optional: true, 16 | Computed: true, 17 | }, 18 | "scope": { 19 | Type: schema.TypeString, 20 | Optional: true, 21 | Computed: true, 22 | }, 23 | "id": { 24 | Type: schema.TypeInt, 25 | Optional: true, 26 | Computed: true, 27 | }, 28 | "project_id": { 29 | Type: schema.TypeInt, 30 | Optional: true, 31 | Computed: true, 32 | }, 33 | "description": { 34 | Type: schema.TypeString, 35 | Optional: true, 36 | Computed: true, 37 | }, 38 | "color": { 39 | Type: schema.TypeString, 40 | Optional: true, 41 | Computed: true, 42 | }, 43 | "deleted": { 44 | Type: schema.TypeBool, 45 | Optional: true, 46 | Computed: true, 47 | }, 48 | }, 49 | Read: dataSourceLabelRead, 50 | } 51 | } 52 | 53 | func dataSourceLabelRead(d *schema.ResourceData, m interface{}) error { 54 | if _, ok := d.GetOk("name"); ok { 55 | if _, ok := d.GetOk("scope"); ok { 56 | registry, err := findLabelByNameAndScope(d, m) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | if err := setLabelSchema(d, registry); err != nil { 62 | return err 63 | } 64 | 65 | return nil 66 | } 67 | } 68 | 69 | if labelID, ok := d.GetOk("id"); ok { 70 | d.SetId(strconv.Itoa(labelID.(int))) 71 | 72 | label, err := findLabelByID(d, m) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if err := setLabelSchema(d, label); err != nil { 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | return errors.New("please specify a combination of name and scope or Id to lookup for a label") 85 | } 86 | -------------------------------------------------------------------------------- /harbor/data_source_label_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 8 | ) 9 | 10 | func init() { 11 | resource.AddTestSweepers("data_source_label", &resource.Sweeper{ 12 | Name: "harbor_label", 13 | }) 14 | } 15 | func TestAccHarborDataSourceLabel(t *testing.T) { 16 | var project models.Project 17 | 18 | var label models.Label 19 | 20 | resource.Test(t, resource.TestCase{ 21 | PreCheck: func() { testAccHarborPreCheck(t) }, 22 | Providers: testAccProviders, 23 | Steps: []resource.TestStep{ 24 | { 25 | Config: testAccHarborCheckGlobalLabelDataSourceConfig(), 26 | Check: resource.ComposeTestCheckFunc( 27 | testAccCheckLabelExists("harbor_label.label_global", &label), 28 | resource.TestCheckResourceAttr( 29 | "data.harbor_label.ds_1", "name", "global-acc-ds-golang"), 30 | resource.TestCheckResourceAttr( 31 | "data.harbor_label.ds_1", "scope", "g"), 32 | resource.TestCheckResourceAttr( 33 | "data.harbor_label.ds_1", "description", "Golang Global Acc Test Label"), 34 | resource.TestCheckResourceAttr( 35 | "data.harbor_label.ds_1", "color", "#333333"), 36 | ), 37 | }, 38 | { 39 | Config: testAccHarborCheckProjectLabelDataSourceConfig(), 40 | Check: resource.ComposeTestCheckFunc( 41 | 42 | testAccHarborCheckProjectExists("harbor_project.project", &project), 43 | testAccCheckLabelExists("harbor_label.project_label", &label), 44 | resource.TestCheckResourceAttr( 45 | "data.harbor_label.ds_p_1", "name", "project-label-acc-golang"), 46 | resource.TestCheckResourceAttr( 47 | "data.harbor_label.ds_p_1", "scope", "p"), 48 | resource.TestCheckResourceAttr( 49 | "data.harbor_label.ds_p_1", "description", "Golang Project Acc Test Label"), 50 | resource.TestCheckResourceAttr( 51 | "data.harbor_label.ds_p_1", "color", "#333333"), 52 | resource.TestCheckResourceAttr( 53 | "data.harbor_label.ds_p_2", "name", "project-label-acc-golang"), 54 | resource.TestCheckResourceAttr( 55 | "data.harbor_label.ds_p_2", "scope", "p"), 56 | resource.TestCheckResourceAttr( 57 | "data.harbor_label.ds_p_2", "description", "Golang Project Acc Test Label"), 58 | resource.TestCheckResourceAttr( 59 | "data.harbor_label.ds_p_2", "color", "#333333"), 60 | ), 61 | }, 62 | }, 63 | }) 64 | } 65 | 66 | func testAccHarborCheckGlobalLabelDataSourceConfig() string { 67 | return ` 68 | resource "harbor_label" "label_global" { 69 | name = "global-acc-ds-golang" 70 | description = "Golang Global Acc Test Label" 71 | color = "#333333" 72 | scope = "g" 73 | } 74 | 75 | data "harbor_label" "ds_1" { 76 | name = harbor_label.label_global.name 77 | scope = "g" 78 | } 79 | ` 80 | } 81 | 82 | func testAccHarborCheckProjectLabelDataSourceConfig() string { 83 | return ` 84 | resource "harbor_project" "project" { 85 | name = "acc-project-label-test" 86 | public = false 87 | vulnerability_scanning = false 88 | } 89 | 90 | resource "harbor_label" "project_label" { 91 | name = "project-label-acc-golang" 92 | description = "Golang Project Acc Test Label" 93 | color = "#333333" 94 | scope = "p" 95 | project_id = harbor_project.project.id 96 | } 97 | data "harbor_label" "ds_p_1" { 98 | id = harbor_label.project_label.id 99 | } 100 | 101 | data "harbor_label" "ds_p_2" { 102 | project_id = harbor_project.project.id 103 | name = harbor_label.project_label.name 104 | scope = "p" 105 | } 106 | ` 107 | } 108 | -------------------------------------------------------------------------------- /harbor/data_source_project.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 10 | ) 11 | 12 | func dataSourceProject() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Optional: true, 18 | Computed: true, 19 | }, 20 | "id": { 21 | Type: schema.TypeInt, 22 | Optional: true, 23 | Computed: true, 24 | }, 25 | "public": { 26 | Type: schema.TypeBool, 27 | Optional: true, 28 | Computed: true, 29 | }, 30 | "vulnerability_scanning": { 31 | Type: schema.TypeBool, 32 | Optional: true, 33 | Computed: true, 34 | }, 35 | "reuse_sys_cve_whitelist": { 36 | Type: schema.TypeBool, 37 | Optional: true, 38 | Default: true, 39 | }, 40 | "cve_whitelist": { 41 | Type: schema.TypeList, 42 | Optional: true, 43 | Elem: &schema.Schema{Type: schema.TypeString}, 44 | }, 45 | }, 46 | 47 | Read: dataSourceProjectRead, 48 | } 49 | } 50 | 51 | func dataSourceProjectRead(d *schema.ResourceData, m interface{}) error { 52 | apiClient := m.(*client.Harbor) 53 | 54 | if _, ok := d.GetOk("name"); ok { 55 | project, err := findProjectByName(d, m) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | if err = setProjectSchema(d, project); err != nil { 61 | return err 62 | } 63 | 64 | return nil 65 | } 66 | 67 | if id, ok := d.GetOk("id"); ok { 68 | params := products.NewGetProjectsProjectIDParams().WithProjectID(int64(id.(int))) 69 | 70 | resp, err := apiClient.Products.GetProjectsProjectID(params, nil) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if err = setProjectSchema(d, resp.Payload); err != nil { 76 | return err 77 | } 78 | 79 | return nil 80 | } 81 | 82 | d.SetId("") 83 | 84 | return fmt.Errorf("please specify a name to lookup for a project") 85 | } 86 | -------------------------------------------------------------------------------- /harbor/data_source_project_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 8 | ) 9 | 10 | func init() { 11 | resource.AddTestSweepers("data_source_project", &resource.Sweeper{ 12 | Name: "harbor_project", 13 | }) 14 | } 15 | func TestAccHarborDataSourceProject(t *testing.T) { 16 | var project models.Project 17 | 18 | resource.Test(t, resource.TestCase{ 19 | PreCheck: func() { testAccHarborPreCheck(t) }, 20 | Providers: testAccProviders, 21 | Steps: []resource.TestStep{ 22 | { 23 | Config: testAccHarborCheckGlobalProjectDataSourceConfig(), 24 | Check: resource.ComposeTestCheckFunc( 25 | testAccHarborCheckProjectExists("harbor_project.project", &project), 26 | resource.TestCheckResourceAttr( 27 | "data.harbor_project.ds_1", "name", "acc-project-ds-test"), 28 | resource.TestCheckResourceAttr( 29 | "data.harbor_project.ds_1", "vulnerability_scanning", "false"), 30 | resource.TestCheckResourceAttr( 31 | "data.harbor_project.ds_1", "public", "false"), 32 | resource.TestCheckResourceAttr( 33 | "data.harbor_project.ds_2", "name", "acc-project-ds-test"), 34 | resource.TestCheckResourceAttr( 35 | "data.harbor_project.ds_2", "vulnerability_scanning", "false"), 36 | resource.TestCheckResourceAttr( 37 | "data.harbor_project.ds_2", "public", "false"), 38 | ), 39 | }, 40 | }, 41 | }) 42 | } 43 | 44 | func testAccHarborCheckGlobalProjectDataSourceConfig() string { 45 | return ` 46 | resource "harbor_project" "project" { 47 | name = "acc-project-ds-test" 48 | public = false 49 | vulnerability_scanning = false 50 | } 51 | 52 | data "harbor_project" "ds_1" { 53 | id = harbor_project.project.id 54 | } 55 | 56 | data "harbor_project" "ds_2" { 57 | name = harbor_project.project.name 58 | } 59 | ` 60 | } 61 | -------------------------------------------------------------------------------- /harbor/data_source_registry.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 8 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 9 | ) 10 | 11 | func dataSourceRegistry() *schema.Resource { 12 | return &schema.Resource{ 13 | Schema: map[string]*schema.Schema{ 14 | "name": { 15 | Type: schema.TypeString, 16 | Optional: true, 17 | Computed: true, 18 | }, 19 | "id": { 20 | Type: schema.TypeInt, 21 | Optional: true, 22 | Computed: true, 23 | }, 24 | "url": { 25 | Type: schema.TypeString, 26 | Optional: true, 27 | Computed: true, 28 | }, 29 | "type": { 30 | Type: schema.TypeString, 31 | Optional: true, 32 | Computed: true, 33 | }, 34 | "description": { 35 | Type: schema.TypeString, 36 | Optional: true, 37 | Computed: true, 38 | }, 39 | "insecure": { 40 | Type: schema.TypeBool, 41 | Optional: true, 42 | Computed: true, 43 | }, 44 | "access_key": { 45 | Type: schema.TypeString, 46 | Optional: true, 47 | }, 48 | "access_secret": { 49 | Type: schema.TypeString, 50 | Optional: true, 51 | Sensitive: true, 52 | }, 53 | "credential_type": { 54 | Type: schema.TypeString, 55 | Optional: true, 56 | }, 57 | }, 58 | Read: dataSourceRegistryRead, 59 | } 60 | } 61 | 62 | func dataSourceRegistryRead(d *schema.ResourceData, m interface{}) error { 63 | apiClient := m.(*client.Harbor) 64 | 65 | if _, ok := d.GetOk("name"); ok { 66 | registry, err := findRegistryByName(d, m) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | if err := setRegistrySchema(d, registry); err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | } 77 | 78 | if id, ok := d.GetOk("id"); ok { 79 | resp, err := apiClient.Products.GetRegistriesID(products.NewGetRegistriesIDParams().WithID(int64(id.(int))), nil) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | if err := setRegistrySchema(d, resp.Payload); err != nil { 85 | return err 86 | } 87 | 88 | return nil 89 | } 90 | 91 | return fmt.Errorf("please specify a name to lookup for a registries") 92 | } 93 | -------------------------------------------------------------------------------- /harbor/data_source_registry_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 8 | ) 9 | 10 | func init() { 11 | resource.AddTestSweepers("data_source_registry", &resource.Sweeper{ 12 | Name: "harbor_registry", 13 | }) 14 | } 15 | func TestAccHarborDataSourceRegistry(t *testing.T) { 16 | var registry models.Registry 17 | 18 | resource.Test(t, resource.TestCase{ 19 | PreCheck: func() { testAccHarborPreCheck(t) }, 20 | Providers: testAccProviders, 21 | Steps: []resource.TestStep{ 22 | { 23 | Config: testAccHarborCheckGlobalRegistryDataSourceConfig(), 24 | Check: resource.ComposeTestCheckFunc( 25 | testAccHarborCheckRegistryExists("harbor_registry.registry", ®istry), 26 | resource.TestCheckResourceAttr( 27 | "data.harbor_registry.ds_1", "name", "acc-registry-ds-test"), 28 | resource.TestCheckResourceAttr( 29 | "data.harbor_registry.ds_1", "url", "https://hub.helm.sh"), 30 | resource.TestCheckResourceAttr( 31 | "data.harbor_registry.ds_1", "type", "helm-hub"), 32 | resource.TestCheckResourceAttr( 33 | "data.harbor_registry.ds_1", "insecure", "false"), 34 | resource.TestCheckResourceAttr( 35 | "data.harbor_registry.ds_2", "name", "acc-registry-ds-test"), 36 | resource.TestCheckResourceAttr( 37 | "data.harbor_registry.ds_2", "url", "https://hub.helm.sh"), 38 | resource.TestCheckResourceAttr( 39 | "data.harbor_registry.ds_2", "type", "helm-hub"), 40 | resource.TestCheckResourceAttr( 41 | "data.harbor_registry.ds_2", "insecure", "false"), 42 | ), 43 | }, 44 | }, 45 | }) 46 | } 47 | 48 | func testAccHarborCheckGlobalRegistryDataSourceConfig() string { 49 | return ` 50 | resource "harbor_registry" "registry" { 51 | name = "acc-registry-ds-test" 52 | url = "https://hub.helm.sh" 53 | type = "helm-hub" 54 | description = "Helm Hub Registry" 55 | insecure = false 56 | } 57 | 58 | data "harbor_registry" "ds_1" { 59 | id = harbor_registry.registry.id 60 | } 61 | 62 | data "harbor_registry" "ds_2" { 63 | name = harbor_registry.registry.name 64 | } 65 | 66 | ` 67 | } 68 | -------------------------------------------------------------------------------- /harbor/provider.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 7 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 8 | "github.com/nolte/terraform-provider-harbor/client" 9 | ) 10 | 11 | func Provider() terraform.ResourceProvider { 12 | return &schema.Provider{ 13 | Schema: map[string]*schema.Schema{ 14 | "host": { 15 | Type: schema.TypeString, 16 | Optional: true, 17 | DefaultFunc: schema.EnvDefaultFunc("HARBOR_ENDPOINT", nil), 18 | }, 19 | "username": { 20 | Type: schema.TypeString, 21 | Required: true, 22 | DefaultFunc: schema.EnvDefaultFunc("HARBOR_USERNAME", nil), 23 | }, 24 | "password": { 25 | Type: schema.TypeString, 26 | Required: true, 27 | DefaultFunc: schema.EnvDefaultFunc("HARBOR_PASSWORD", nil), 28 | }, 29 | "schema": { 30 | Type: schema.TypeString, 31 | Optional: true, 32 | Default: "https", 33 | }, 34 | "insecure": { 35 | Type: schema.TypeString, 36 | Optional: true, 37 | DefaultFunc: schema.EnvDefaultFunc("HARBOR_INSECURE", "false"), 38 | }, 39 | "basepath": { 40 | Type: schema.TypeString, 41 | Required: true, 42 | DefaultFunc: schema.EnvDefaultFunc("HARBOR_BASEPATH", "/api"), 43 | }, 44 | }, 45 | 46 | ResourcesMap: map[string]*schema.Resource{ 47 | "harbor_project_member": resourceProjectMember(), 48 | "harbor_project": resourceProject(), 49 | "harbor_registry": resourceRegistry(), 50 | "harbor_config_email": resourceConfigEmail(), 51 | "harbor_config_auth": resourceConfigAuth(), 52 | "harbor_config_system": resourceConfigSystem(), 53 | "harbor_robot_account": resourceRobotAccount(), 54 | "harbor_tasks": resourceTasks(), 55 | "harbor_label": resourceLabel(), 56 | "harbor_replication": resourceReplication(), 57 | "harbor_usergroup": resourceUsergroup(), 58 | "harbor_retention_policy": resourceRetentionPolicy(), 59 | }, 60 | DataSourcesMap: map[string]*schema.Resource{ 61 | "harbor_project": dataSourceProject(), 62 | "harbor_registry": dataSourceRegistry(), 63 | "harbor_label": dataSourceLabel(), 64 | }, 65 | ConfigureFunc: providerConfigure, 66 | } 67 | } 68 | 69 | func providerConfigure(d *schema.ResourceData) (interface{}, error) { 70 | host := d.Get("host").(string) 71 | username := d.Get("username").(string) 72 | password := d.Get("password").(string) 73 | 74 | insecure, err := strconv.ParseBool(d.Get("insecure").(string)) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | basepath := d.Get("basepath").(string) 80 | schema := d.Get("schema").(string) 81 | 82 | return client.NewClient(host, username, password, insecure, basepath, schema), nil 83 | } 84 | -------------------------------------------------------------------------------- /harbor/provider_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 8 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 9 | "github.com/nolte/terraform-provider-harbor/harbor" 10 | ) 11 | 12 | var testAccProviders map[string]terraform.ResourceProvider 13 | var testAccProvider *schema.Provider 14 | 15 | func init() { 16 | testAccProvider = harbor.Provider().(*schema.Provider) 17 | testAccProviders = map[string]terraform.ResourceProvider{ 18 | "harbor": testAccProvider, 19 | } 20 | } 21 | 22 | func TestProvider(t *testing.T) { 23 | if err := harbor.Provider().(*schema.Provider).InternalValidate(); err != nil { 24 | t.Fatalf("err: %s", err) 25 | } 26 | } 27 | 28 | func TestProvider_impl(t *testing.T) { 29 | var _ terraform.ResourceProvider = harbor.Provider() 30 | } 31 | 32 | func testAccHarborPreCheck(t *testing.T) { 33 | if v := os.Getenv("HARBOR_ENDPOINT"); v == "" { 34 | t.Fatal("HARBOR_ENDPOINT must be set for acceptance tests") 35 | } 36 | 37 | if v := os.Getenv("HARBOR_USERNAME"); v == "" { 38 | t.Fatal("HARBOR_USERNAME must be set for acceptance tests") 39 | } 40 | 41 | if v := os.Getenv("HARBOR_PASSWORD"); v == "" { 42 | t.Fatal("HARBOR_PASSWORD must be set for acceptance tests") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /harbor/resource_config_auth.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 12 | ) 13 | 14 | func resourceConfigAuth() *schema.Resource { 15 | return &schema.Resource{ 16 | Schema: map[string]*schema.Schema{ 17 | "auth_mode": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "oidc_name": { 22 | Type: schema.TypeString, 23 | Optional: true, 24 | }, 25 | "oidc_endpoint": { 26 | Type: schema.TypeString, 27 | Optional: true, 28 | }, 29 | "oidc_client_id": { 30 | Type: schema.TypeString, 31 | Optional: true, 32 | }, 33 | "oidc_client_secret": { 34 | Type: schema.TypeString, 35 | Optional: true, 36 | Sensitive: true, 37 | }, 38 | "oidc_groups_claim": { 39 | Type: schema.TypeString, 40 | Optional: true, 41 | }, 42 | "oidc_scope": { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | }, 46 | "oidc_verify_cert": { 47 | Type: schema.TypeBool, 48 | Optional: true, 49 | }, 50 | }, 51 | Create: resourceConfigAuthUpdate, 52 | Read: resourceConfigAuthRead, 53 | Update: resourceConfigAuthUpdate, 54 | Delete: resourceConfigAuthDelete, 55 | } 56 | } 57 | 58 | func resourceConfigAuthRead(d *schema.ResourceData, m interface{}) error { 59 | apiClient := m.(*client.Harbor) 60 | resp, err := apiClient.Products.GetConfigurations(products.NewGetConfigurationsParams(), nil) 61 | 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | if err := d.Set("auth_mode", resp.Payload.AuthMode.Value); err != nil { 67 | return err 68 | } 69 | 70 | if err := d.Set("oidc_name", resp.Payload.OidcName.Value); err != nil { 71 | return err 72 | } 73 | 74 | if err := d.Set("oidc_endpoint", resp.Payload.OidcEndpoint.Value); err != nil { 75 | return err 76 | } 77 | 78 | if err := d.Set("oidc_client_id", resp.Payload.OidcClientID.Value); err != nil { 79 | return err 80 | } 81 | 82 | if nil != resp.Payload.OidcGroupsClaim { 83 | if err := d.Set("oidc_groups_claim", resp.Payload.OidcGroupsClaim.Value); err != nil { 84 | return err 85 | } 86 | } 87 | 88 | if err := d.Set("oidc_scope", resp.Payload.OidcScope.Value); err != nil { 89 | return err 90 | } 91 | 92 | if err := d.Set("oidc_verify_cert", resp.Payload.OidcVerifyCert.Value); err != nil { 93 | return err 94 | } 95 | 96 | return nil 97 | } 98 | 99 | func resourceConfigAuthUpdate(d *schema.ResourceData, m interface{}) error { 100 | apiClient, body := newAPIClient(d, m) 101 | 102 | _, err := apiClient.Products.PutConfigurations(products.NewPutConfigurationsParams().WithConfigurations(&body), nil) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", d.Get("oidc_name").(string)))) 108 | 109 | return resourceConfigAuthRead(d, m) 110 | } 111 | 112 | func resourceConfigAuthDelete(d *schema.ResourceData, m interface{}) error { 113 | return nil 114 | } 115 | 116 | func newAPIClient(d *schema.ResourceData, m interface{}) (*client.Harbor, models.Configurations) { 117 | apiClient := m.(*client.Harbor) 118 | 119 | body := models.Configurations{ 120 | AuthMode: d.Get("auth_mode").(string), 121 | OidcName: d.Get("oidc_name").(string), 122 | OidcEndpoint: d.Get("oidc_endpoint").(string), 123 | OidcClientID: d.Get("oidc_client_id").(string), 124 | OidcClientSecret: d.Get("oidc_client_secret").(string), 125 | OidcGroupsClaim: d.Get("oidc_groups_claim").(string), 126 | OidcScope: d.Get("oidc_scope").(string), 127 | OidcVerifyCert: d.Get("oidc_verify_cert").(bool), 128 | } 129 | 130 | return apiClient, body 131 | } 132 | -------------------------------------------------------------------------------- /harbor/resource_config_email.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 12 | ) 13 | 14 | func resourceConfigEmail() *schema.Resource { 15 | return &schema.Resource{ 16 | Schema: map[string]*schema.Schema{ 17 | "email_host": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "email_port": { 22 | Type: schema.TypeInt, 23 | Required: true, 24 | }, 25 | "email_username": { 26 | Type: schema.TypeString, 27 | Optional: true, 28 | }, 29 | "email_password": { 30 | Type: schema.TypeString, 31 | Optional: true, 32 | Sensitive: true, 33 | }, 34 | "email_from": { 35 | Type: schema.TypeString, 36 | Required: true, 37 | }, 38 | "email_ssl": { 39 | Type: schema.TypeBool, 40 | Optional: true, 41 | Default: true, 42 | }, 43 | // "email_verify_cert": { 44 | // Type: schema.TypeString, 45 | // Optional: true, 46 | // }, 47 | }, 48 | Create: resourceConfigEmailCreate, 49 | Read: resourceConfigEmailRead, 50 | Update: resourceConfigEmailCreate, 51 | Delete: resourceConfigEmailDelete, 52 | } 53 | } 54 | 55 | func resourceConfigEmailCreate(d *schema.ResourceData, m interface{}) error { 56 | apiClient := m.(*client.Harbor) 57 | params := products.NewPutConfigurationsParams().WithConfigurations(&models.Configurations{ 58 | EmailHost: d.Get("email_host").(string), 59 | EmailPort: int64(d.Get("email_port").(int)), 60 | EmailUsername: d.Get("email_username").(string), 61 | EmailPassword: d.Get("email_password").(string), 62 | EmailFrom: d.Get("email_from").(string), 63 | EmailSsl: d.Get("email_ssl").(bool), 64 | }) 65 | 66 | _, err := apiClient.Products.PutConfigurations(params, nil) 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | 71 | d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", d.Get("email_host").(string)))) 72 | 73 | return resourceConfigEmailRead(d, m) 74 | } 75 | 76 | func resourceConfigEmailRead(d *schema.ResourceData, m interface{}) error { 77 | apiClient := m.(*client.Harbor) 78 | 79 | resp, err := apiClient.Products.GetConfigurations(products.NewGetConfigurationsParams(), nil) 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | 84 | if err := d.Set("email_host", resp.Payload.EmailHost.Value); err != nil { 85 | return err 86 | } 87 | 88 | if err := d.Set("email_port", int(resp.Payload.EmailPort.Value)); err != nil { 89 | return err 90 | } 91 | 92 | if err := d.Set("email_username", resp.Payload.EmailUsername.Value); err != nil { 93 | return err 94 | } 95 | 96 | if err := d.Set("email_from", resp.Payload.EmailFrom.Value); err != nil { 97 | return err 98 | } 99 | 100 | if err := d.Set("email_ssl", resp.Payload.EmailSsl.Value); err != nil { 101 | return err 102 | } 103 | 104 | return nil 105 | } 106 | 107 | func resourceConfigEmailDelete(d *schema.ResourceData, m interface{}) error { 108 | log.Print("not supported at the moment ... the problem is set empty values to the config") 109 | return nil 110 | } 111 | -------------------------------------------------------------------------------- /harbor/resource_config_system.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 12 | ) 13 | 14 | const robotTokenExpirationDefault = 30 15 | 16 | func resourceConfigSystem() *schema.Resource { 17 | return &schema.Resource{ 18 | Schema: map[string]*schema.Schema{ 19 | "project_creation_restriction": { 20 | Type: schema.TypeString, 21 | Optional: true, 22 | Default: "adminonly", 23 | }, 24 | "read_only": { 25 | Type: schema.TypeBool, 26 | Optional: true, 27 | Default: false, 28 | }, 29 | "robot_token_expiration": { 30 | Type: schema.TypeInt, 31 | Optional: true, 32 | Default: robotTokenExpirationDefault, 33 | }, 34 | }, 35 | Create: resourceConfigSystemUpdate, 36 | Read: resourceConfigSystemRead, 37 | Update: resourceConfigSystemUpdate, 38 | Delete: resourceConfigSystemDelete, 39 | } 40 | } 41 | 42 | func resourceConfigSystemRead(d *schema.ResourceData, m interface{}) error { 43 | apiClient := m.(*client.Harbor) 44 | 45 | resp, err := apiClient.Products.GetConfigurations(products.NewGetConfigurationsParams(), nil) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | // some internal harbor process will be change the value to project_creation_restriction = "oidc_auth" 50 | //if err := d.Set("project_creation_restriction", resp.Payload.AuthMode.Value); err != nil { 51 | // return err 52 | //} 53 | if nil != resp.Payload.ReadOnly { 54 | if err := d.Set("read_only", resp.Payload.ReadOnly.Value); err != nil { 55 | return err 56 | } 57 | } else { 58 | if err := d.Set("read_only", false); err != nil { 59 | return err 60 | } 61 | } 62 | 63 | if err := d.Set("robot_token_expiration", resp.Payload.TokenExpiration.Value); err != nil { 64 | return err 65 | } 66 | 67 | d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", "systemconfig"))) 68 | 69 | return nil 70 | } 71 | 72 | func resourceConfigSystemUpdate(d *schema.ResourceData, m interface{}) error { 73 | apiClient := m.(*client.Harbor) 74 | params := products.NewPutConfigurationsParams().WithConfigurations(&models.Configurations{ 75 | ProjectCreationRestriction: d.Get("project_creation_restriction").(string), 76 | TokenExpiration: int64(d.Get("robot_token_expiration").(int)), 77 | ReadOnly: d.Get("read_only").(bool), 78 | }) 79 | 80 | _, err := apiClient.Products.PutConfigurations(params, nil) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | return resourceConfigSystemRead(d, m) 86 | } 87 | 88 | func resourceConfigSystemDelete(d *schema.ResourceData, m interface{}) error { 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /harbor/resource_labels.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 12 | ) 13 | 14 | func resourceLabel() *schema.Resource { 15 | return &schema.Resource{ 16 | Schema: map[string]*schema.Schema{ 17 | "name": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "project_id": { 22 | Type: schema.TypeInt, 23 | Optional: true, 24 | Default: 0, 25 | ForceNew: true, 26 | }, 27 | "description": { 28 | Type: schema.TypeString, 29 | Optional: true, 30 | Default: "", 31 | }, 32 | "color": { 33 | Type: schema.TypeString, 34 | Optional: true, 35 | Default: "#61717D", 36 | }, 37 | "deleted": { 38 | Type: schema.TypeBool, 39 | Optional: true, 40 | Default: false, 41 | }, 42 | "scope": { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | Default: "g", 46 | ForceNew: true, 47 | ValidateFunc: validateLabelGroup, 48 | }, 49 | }, 50 | Create: resourceLabelCreate, 51 | Read: resourceLabelRead, 52 | Update: resourceLabelUpdate, 53 | Delete: resourceLabelDelete, 54 | Importer: &schema.ResourceImporter{ 55 | State: schema.ImportStatePassthrough, 56 | }, 57 | } 58 | } 59 | 60 | func validateLabelGroup(v interface{}, k string) (ws []string, errors []error) { 61 | value := v.(string) 62 | 63 | if value != "g" && value != "p" { 64 | errors = append(errors, fmt.Errorf("label group must be 'p' or 'g' %s", k)) 65 | return 66 | } 67 | 68 | return 69 | } 70 | 71 | func buildLabel(d *schema.ResourceData) *models.Label { 72 | return &models.Label{ 73 | Color: d.Get("color").(string), 74 | Scope: d.Get("scope").(string), 75 | ProjectID: int64(d.Get("project_id").(int)), 76 | Deleted: d.Get("deleted").(bool), 77 | Name: d.Get("name").(string), 78 | Description: d.Get("description").(string), 79 | } 80 | } 81 | func resourceLabelCreate(d *schema.ResourceData, m interface{}) error { 82 | apiClient := m.(*client.Harbor) 83 | 84 | body := products.NewPostLabelsParams().WithLabel(buildLabel(d)) 85 | 86 | _, err := apiClient.Products.PostLabels(body, nil) 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | //d.Set("Label_id", resp.Payload[0].LabelID) 91 | label, err := findLabelByNameAndScope(d, m) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | d.SetId(strconv.Itoa(int(label.ID))) 97 | 98 | return resourceLabelRead(d, m) 99 | } 100 | 101 | func findLabelByNameAndScope(d *schema.ResourceData, m interface{}) (*models.Label, error) { 102 | apiClient := m.(*client.Harbor) 103 | 104 | if name, ok := d.GetOk("name"); ok { 105 | if scope, ok := d.GetOk("scope"); ok { 106 | searchName := name.(string) 107 | scopeName := scope.(string) 108 | 109 | query := products.NewGetLabelsParams().WithScope(scopeName).WithName(&searchName) 110 | 111 | if scopeName == "p" { 112 | projectID := int64(d.Get("project_id").(int)) 113 | query = query.WithProjectID(&projectID) 114 | } 115 | 116 | resp, err := apiClient.Products.GetLabels(query, nil) 117 | if err != nil { 118 | d.SetId("") 119 | return &models.Label{}, err 120 | } 121 | 122 | for _, element := range resp.Payload { 123 | if element.Name == searchName { 124 | return element, nil 125 | } 126 | } 127 | 128 | return &models.Label{}, fmt.Errorf("no label found with name %v", searchName) 129 | } 130 | } 131 | 132 | return &models.Label{}, fmt.Errorf("fail to lookup label by Name and Scope") 133 | } 134 | 135 | func findLabelByID(d *schema.ResourceData, m interface{}) (*models.Label, error) { 136 | apiClient := m.(*client.Harbor) 137 | 138 | if searchID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 139 | query := products.NewGetLabelsIDParams().WithID(searchID) 140 | 141 | resp, err := apiClient.Products.GetLabelsID(query, nil) 142 | if err != nil { 143 | return &models.Label{}, err 144 | } 145 | 146 | return resp.Payload, nil 147 | } 148 | 149 | return &models.Label{}, fmt.Errorf("fail to find the label") 150 | } 151 | 152 | func setLabelSchema(d *schema.ResourceData, resp *models.Label) error { 153 | d.SetId(strconv.Itoa(int(resp.ID))) 154 | 155 | if err := d.Set("name", resp.Name); err != nil { 156 | return err 157 | } 158 | 159 | if err := d.Set("description", resp.Description); err != nil { 160 | return err 161 | } 162 | 163 | if err := d.Set("deleted", resp.Deleted); err != nil { 164 | return err 165 | } 166 | 167 | if err := d.Set("color", resp.Color); err != nil { 168 | return err 169 | } 170 | 171 | if err := d.Set("scope", resp.Scope); err != nil { 172 | return err 173 | } 174 | 175 | if err := d.Set("project_id", int(resp.ProjectID)); err != nil { 176 | return err 177 | } 178 | 179 | return nil 180 | } 181 | func resourceLabelRead(d *schema.ResourceData, m interface{}) error { 182 | label, err := findLabelByID(d, m) 183 | if err != nil { 184 | return err 185 | } 186 | 187 | if err := setLabelSchema(d, label); err != nil { 188 | return err 189 | } 190 | 191 | return nil 192 | } 193 | 194 | func resourceLabelUpdate(d *schema.ResourceData, m interface{}) error { 195 | apiClient := m.(*client.Harbor) 196 | 197 | if resourceID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 198 | body := products.NewPutLabelsIDParams().WithLabel(buildLabel(d)).WithID(resourceID) 199 | if _, err := apiClient.Products.PutLabelsID(body, nil); err != nil { 200 | return err 201 | } 202 | 203 | return resourceLabelRead(d, m) 204 | } 205 | 206 | return fmt.Errorf("label Id not a Integer") 207 | } 208 | 209 | func resourceLabelDelete(d *schema.ResourceData, m interface{}) error { 210 | apiClient := m.(*client.Harbor) 211 | 212 | if resourceID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 213 | delete := products.NewDeleteLabelsIDParams().WithID(resourceID) 214 | if _, err := apiClient.Products.DeleteLabelsID(delete, nil); err != nil { 215 | return err 216 | } 217 | 218 | d.SetId("") 219 | 220 | return nil 221 | } 222 | 223 | return fmt.Errorf("label Id not a Integer") 224 | } 225 | -------------------------------------------------------------------------------- /harbor/resource_labels_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 13 | ) 14 | 15 | func init() { 16 | resource.AddTestSweepers("resource_harbor_label", &resource.Sweeper{ 17 | Name: "harbor_label", 18 | }) 19 | } 20 | 21 | func TestAccHarborLabel_Basic(t *testing.T) { 22 | var label models.Label 23 | 24 | resource.Test(t, resource.TestCase{ 25 | PreCheck: func() { testAccHarborPreCheck(t) }, 26 | Providers: testAccProviders, 27 | Steps: []resource.TestStep{ 28 | { 29 | Config: testAccHarborCheckGlobalLabelResourceConfig(), 30 | Check: resource.ComposeTestCheckFunc( 31 | testAccCheckLabelExists("harbor_label.main", &label), 32 | resource.TestCheckResourceAttr( 33 | "harbor_label.main", "name", "global-acc-golang"), 34 | resource.TestCheckResourceAttr( 35 | "harbor_label.main", "description", "Golang Acc Test Label"), 36 | resource.TestCheckResourceAttr( 37 | "harbor_label.main", "scope", "g"), 38 | resource.TestCheckResourceAttr( 39 | "harbor_label.main", "color", "#61717D"), 40 | resource.TestCheckResourceAttr( 41 | "harbor_label.main", "description", "Golang Acc Test Label"), 42 | ), 43 | }, 44 | { 45 | Config: testAccHarborCheckProjectLabelResourceConfig(), 46 | Check: resource.ComposeTestCheckFunc( 47 | testAccCheckLabelExists("harbor_label.main_project", &label), 48 | resource.TestCheckResourceAttr( 49 | "harbor_label.main_project", "name", "project-acc-golang"), 50 | resource.TestCheckResourceAttr( 51 | "harbor_label.main_project", "scope", "p"), 52 | resource.TestCheckResourceAttr( 53 | "harbor_label.main_project", "color", "#333333"), 54 | resource.TestCheckResourceAttr( 55 | "harbor_label.main_project", "description", "Golang Project Acc Test Label"), 56 | ), 57 | }, 58 | }, 59 | }) 60 | } 61 | 62 | func testAccHarborCheckGlobalLabelResourceConfig() string { 63 | return ` 64 | resource "harbor_label" "main" { 65 | name = "global-acc-golang" 66 | description = "Golang Acc Test Label" 67 | color = "#61717D" 68 | } 69 | ` 70 | } 71 | 72 | func testAccHarborCheckProjectLabelResourceConfig() string { 73 | return ` 74 | resource "harbor_project" "main" { 75 | name = "acc-label-test" 76 | public = false 77 | vulnerability_scanning = false 78 | } 79 | 80 | resource "harbor_label" "main_project" { 81 | name = "project-acc-golang" 82 | description = "Golang Project Acc Test Label" 83 | color = "#333333" 84 | scope = "p" 85 | project_id = harbor_project.main.id 86 | } 87 | ` 88 | } 89 | 90 | func testAccCheckLabelExists(n string, label *models.Label) resource.TestCheckFunc { 91 | return func(s *terraform.State) error { 92 | rs, ok := s.RootModule().Resources[n] 93 | 94 | if !ok { 95 | return fmt.Errorf("Not found: %s", n) 96 | } 97 | 98 | if rs.Primary.ID == "" { 99 | return fmt.Errorf("No Record ID is set") 100 | } 101 | 102 | client := testAccProvider.Meta().(*client.Harbor) 103 | 104 | if searchID, err := strconv.ParseInt(rs.Primary.ID, 10, 64); err == nil { 105 | foundLabel, err := client.Products.GetLabelsID(products.NewGetLabelsIDParams().WithID(searchID), nil) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | if foundLabel == nil { 111 | return fmt.Errorf("Record not found") 112 | } 113 | 114 | *label = *foundLabel.Payload 115 | } 116 | 117 | return nil 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /harbor/resource_project_member_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 8 | ) 9 | 10 | func init() { 11 | resource.AddTestSweepers("resource_harbor_project_member", &resource.Sweeper{ 12 | Name: "harbor_project_member", 13 | }) 14 | } 15 | 16 | func TestAccHarborProjectMember_Basic(t *testing.T) { 17 | var project models.Project 18 | 19 | resource.Test(t, resource.TestCase{ 20 | PreCheck: func() { testAccHarborPreCheck(t) }, 21 | Providers: testAccProviders, 22 | Steps: []resource.TestStep{ 23 | { 24 | Config: testAccHarborCheckProjectMemberResourceConfig(), 25 | Check: resource.ComposeTestCheckFunc( 26 | testAccHarborCheckProjectExists("harbor_project.project_member", &project), 27 | resource.TestCheckResourceAttr( 28 | "harbor_project_member.group_member", "role", "limited_guest"), 29 | resource.TestCheckResourceAttr( 30 | "harbor_project_member.group_member", "group_type", "http"), 31 | resource.TestCheckResourceAttr( 32 | "harbor_project_member.group_member", "group_name", "example_group"), 33 | ), 34 | }, 35 | }, 36 | }) 37 | } 38 | 39 | func testAccHarborCheckProjectMemberResourceConfig() string { 40 | return ` 41 | resource "harbor_project" "project_member" { 42 | name = "acc-project-member-test" 43 | public = false 44 | vulnerability_scanning = false 45 | } 46 | resource "harbor_usergroup" "project_group" { 47 | name = "example_group" 48 | type = "http" 49 | } 50 | resource "harbor_project_member" "group_member" { 51 | project_id = harbor_project.project_member.id 52 | role = "limited_guest" 53 | group_type = "http" 54 | group_name = harbor_usergroup.project_group.name 55 | } 56 | ` 57 | } 58 | -------------------------------------------------------------------------------- /harbor/resource_project_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 13 | ) 14 | 15 | func init() { 16 | resource.AddTestSweepers("resource_harbor_project", &resource.Sweeper{ 17 | Name: "harbor_project", 18 | }) 19 | } 20 | 21 | func TestAccHarborProject_Basic(t *testing.T) { 22 | var project models.Project 23 | 24 | resource.Test(t, resource.TestCase{ 25 | PreCheck: func() { testAccHarborPreCheck(t) }, 26 | Providers: testAccProviders, 27 | Steps: []resource.TestStep{ 28 | { 29 | Config: testAccHarborCheckGlobalProjectResourceConfig(), 30 | Check: resource.ComposeTestCheckFunc( 31 | testAccHarborCheckProjectExists("harbor_project.main", &project), 32 | resource.TestCheckResourceAttr( 33 | "harbor_project.main", "name", "acc-project-test"), 34 | resource.TestCheckResourceAttr( 35 | "harbor_project.main", "public", "false"), 36 | resource.TestCheckResourceAttr( 37 | "harbor_project.main", "vulnerability_scanning", "false"), 38 | ), 39 | }, 40 | { 41 | Config: testAccHarborCheckProjectActiveAllResourceConfig(), 42 | Check: resource.ComposeTestCheckFunc( 43 | testAccHarborCheckProjectExists("harbor_project.main", &project), 44 | resource.TestCheckResourceAttr( 45 | "harbor_project.main", "name", "acc-project-minimal-test"), 46 | resource.TestCheckResourceAttr( 47 | "harbor_project.main", "public", "true"), 48 | resource.TestCheckResourceAttr( 49 | "harbor_project.main", "vulnerability_scanning", "true"), 50 | ), 51 | }, 52 | }, 53 | }) 54 | } 55 | 56 | func testAccHarborCheckGlobalProjectResourceConfig() string { 57 | return ` 58 | resource "harbor_project" "main" { 59 | name = "acc-project-test" 60 | public = false 61 | vulnerability_scanning = false 62 | } 63 | ` 64 | } 65 | func testAccHarborCheckProjectActiveAllResourceConfig() string { 66 | return ` 67 | resource "harbor_project" "main" { 68 | name = "acc-project-minimal-test" 69 | public = true 70 | vulnerability_scanning = true 71 | } 72 | ` 73 | } 74 | 75 | func testAccHarborCheckProjectExists(n string, project *models.Project) resource.TestCheckFunc { 76 | return func(s *terraform.State) error { 77 | rs, ok := s.RootModule().Resources[n] 78 | 79 | if !ok { 80 | return fmt.Errorf("Not found: %s", n) 81 | } 82 | 83 | if rs.Primary.ID == "" { 84 | return fmt.Errorf("No Record ID is set") 85 | } 86 | 87 | client := testAccProvider.Meta().(*client.Harbor) 88 | 89 | if searchID, err := strconv.ParseInt(rs.Primary.ID, 10, 64); err == nil { 90 | params := products.NewGetProjectsProjectIDParams().WithProjectID(searchID) 91 | 92 | foundProject, err := client.Products.GetProjectsProjectID(params, nil) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | if foundProject == nil { 98 | return fmt.Errorf("Record not found") 99 | } 100 | 101 | *project = *foundProject.Payload 102 | } 103 | 104 | return nil 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /harbor/resource_registry.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 8 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 11 | ) 12 | 13 | func resourceRegistry() *schema.Resource { 14 | return &schema.Resource{ 15 | Schema: map[string]*schema.Schema{ 16 | "name": { 17 | Type: schema.TypeString, 18 | Required: true, 19 | }, 20 | "url": { 21 | Type: schema.TypeString, 22 | Required: true, 23 | }, 24 | "type": { 25 | Type: schema.TypeString, 26 | Required: true, 27 | ForceNew: true, 28 | }, 29 | "description": { 30 | Type: schema.TypeString, 31 | Optional: true, 32 | }, 33 | "insecure": { 34 | Type: schema.TypeBool, 35 | Optional: true, 36 | Default: false, 37 | }, 38 | "access_key": { 39 | Type: schema.TypeString, 40 | Optional: true, 41 | }, 42 | "access_secret": { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | Sensitive: true, 46 | }, 47 | "credential_type": { 48 | Type: schema.TypeString, 49 | Optional: true, 50 | }, 51 | }, 52 | Create: resourceRegistryCreate, 53 | Read: resourceRegistryRead, 54 | Update: resourceRegistryUpdate, 55 | Delete: resourceRegistryDelete, 56 | Importer: &schema.ResourceImporter{ 57 | State: schema.ImportStatePassthrough, 58 | }, 59 | } 60 | } 61 | 62 | func resourceRegistryCreate(d *schema.ResourceData, m interface{}) error { 63 | apiClient := m.(*client.Harbor) 64 | 65 | body := products.NewPostRegistriesParams().WithRegistry(&models.Registry{ 66 | Description: d.Get("description").(string), 67 | Insecure: d.Get("insecure").(bool), 68 | Name: d.Get("name").(string), 69 | Type: d.Get("type").(string), 70 | URL: d.Get("url").(string), 71 | Credential: &models.RegistryCredential{ 72 | AccessKey: d.Get("access_key").(string), 73 | AccessSecret: d.Get("access_secret").(string), 74 | Type: d.Get("credential_type").(string), 75 | }, 76 | }) 77 | 78 | _, err := apiClient.Products.PostRegistries(body, nil) 79 | if err != nil { 80 | return err 81 | } 82 | 83 | registry, err := findRegistryByName(d, m) 84 | 85 | if err != nil { 86 | return err 87 | } 88 | 89 | d.SetId(strconv.Itoa(int(registry.ID))) 90 | 91 | return resourceRegistryRead(d, m) 92 | } 93 | func findRegistryByName(d *schema.ResourceData, m interface{}) (*models.Registry, error) { 94 | apiClient := m.(*client.Harbor) 95 | 96 | if name, ok := d.GetOk("name"); ok { 97 | registryName := name.(string) 98 | query := products.NewGetRegistriesParams().WithName(®istryName) 99 | 100 | resp, err := apiClient.Products.GetRegistries(query, nil) 101 | if err != nil { 102 | d.SetId("") 103 | return &models.Registry{}, err 104 | } 105 | for _, element := range resp.Payload { 106 | if element.Name == registryName { 107 | return element, nil 108 | } 109 | } 110 | return &models.Registry{}, fmt.Errorf("no Registry found with name %v", registryName) 111 | } 112 | 113 | return &models.Registry{}, fmt.Errorf("fail to lookup Registry by Name") 114 | } 115 | func resourceRegistryRead(d *schema.ResourceData, m interface{}) error { 116 | apiClient := m.(*client.Harbor) 117 | 118 | if registryID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 119 | resp, err := apiClient.Products.GetRegistriesID(products.NewGetRegistriesIDParams().WithID(registryID), nil) 120 | 121 | if err != nil { 122 | return err 123 | } 124 | 125 | if err = setRegistrySchema(d, resp.Payload); err != nil { 126 | return err 127 | } 128 | 129 | return nil 130 | } 131 | 132 | return fmt.Errorf("registry Id not a Integer currently: '%s'", d.Id()) 133 | } 134 | 135 | func resourceRegistryUpdate(d *schema.ResourceData, m interface{}) error { 136 | apiClient := m.(*client.Harbor) 137 | 138 | if registryID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 139 | params := products.NewPutRegistriesIDParams().WithID(registryID).WithRepoTarget(&models.PutRegistry{ 140 | Description: d.Get("description").(string), 141 | Insecure: d.Get("insecure").(bool), 142 | Name: d.Get("name").(string), 143 | URL: d.Get("url").(string), 144 | AccessKey: d.Get("access_key").(string), 145 | AccessSecret: d.Get("access_secret").(string), 146 | CredentialType: d.Get("credential_type").(string), 147 | }) 148 | if _, err := apiClient.Products.PutRegistriesID(params, nil); err != nil { 149 | return err 150 | } 151 | 152 | return resourceRegistryRead(d, m) 153 | } 154 | 155 | return fmt.Errorf("registry Id not a Integer") 156 | } 157 | 158 | func resourceRegistryDelete(d *schema.ResourceData, m interface{}) error { 159 | apiClient := m.(*client.Harbor) 160 | 161 | if registryID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 162 | params := products.NewDeleteRegistriesIDParams().WithID(registryID) 163 | 164 | if _, err := apiClient.Products.DeleteRegistriesID(params, nil); err != nil { 165 | return err 166 | } 167 | 168 | return nil 169 | } 170 | 171 | return fmt.Errorf("registry Id not a Integer") 172 | } 173 | 174 | func setRegistrySchema(d *schema.ResourceData, registry *models.Registry) error { 175 | d.SetId(strconv.Itoa(int(registry.ID))) 176 | 177 | if err := d.Set("description", registry.Description); err != nil { 178 | return err 179 | } 180 | 181 | if err := d.Set("insecure", registry.Insecure); err != nil { 182 | return err 183 | } 184 | 185 | if err := d.Set("name", registry.Name); err != nil { 186 | return err 187 | } 188 | 189 | if err := d.Set("type", registry.Type); err != nil { 190 | return err 191 | } 192 | 193 | if err := d.Set("url", registry.URL); err != nil { 194 | return err 195 | } 196 | 197 | if err := d.Set("access_key", registry.Credential.AccessKey); err != nil { 198 | return err 199 | } 200 | 201 | if err := d.Set("credential_type", registry.Credential.Type); err != nil { 202 | return err 203 | } 204 | 205 | return nil 206 | } 207 | -------------------------------------------------------------------------------- /harbor/resource_registry_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 13 | ) 14 | 15 | func init() { 16 | resource.AddTestSweepers("resource_harbor_registry", &resource.Sweeper{ 17 | Name: "harbor_registry", 18 | }) 19 | } 20 | 21 | func TestAccHarborRegistry_Basic(t *testing.T) { 22 | var registry models.Registry 23 | 24 | resource.Test(t, resource.TestCase{ 25 | PreCheck: func() { testAccHarborPreCheck(t) }, 26 | Providers: testAccProviders, 27 | Steps: []resource.TestStep{ 28 | { 29 | Config: testAccHarborCheckRegistryResourceConfig(), 30 | Check: resource.ComposeTestCheckFunc( 31 | testAccHarborCheckRegistryExists("harbor_registry.helmhub", ®istry), 32 | resource.TestCheckResourceAttr( 33 | "harbor_registry.helmhub", "name", "acc-registry-test"), 34 | resource.TestCheckResourceAttr( 35 | "harbor_registry.helmhub", "url", "https://hub.helm.sh"), 36 | resource.TestCheckResourceAttr( 37 | "harbor_registry.helmhub", "description", "Helm Hub Registry"), 38 | resource.TestCheckResourceAttr( 39 | "harbor_registry.helmhub", "insecure", "false"), 40 | ), 41 | }, 42 | }, 43 | }) 44 | } 45 | 46 | func testAccHarborCheckRegistryResourceConfig() string { 47 | return ` 48 | resource "harbor_registry" "helmhub" { 49 | name = "acc-registry-test" 50 | url = "https://hub.helm.sh" 51 | type = "helm-hub" 52 | description = "Helm Hub Registry" 53 | insecure = false 54 | } 55 | ` 56 | } 57 | 58 | func testAccHarborCheckRegistryExists(n string, registry *models.Registry) resource.TestCheckFunc { 59 | return func(s *terraform.State) error { 60 | rs, ok := s.RootModule().Resources[n] 61 | 62 | if !ok { 63 | return fmt.Errorf("Not found: %s", n) 64 | } 65 | 66 | if rs.Primary.ID == "" { 67 | return fmt.Errorf("No Record ID is set") 68 | } 69 | 70 | client := testAccProvider.Meta().(*client.Harbor) 71 | 72 | if searchID, err := strconv.ParseInt(rs.Primary.ID, 10, 64); err == nil { 73 | params := products.NewGetRegistriesIDParams().WithID(searchID) 74 | 75 | foundRegistry, err := client.Products.GetRegistriesID(params, nil) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | if foundRegistry == nil { 81 | return fmt.Errorf("Record not found") 82 | } 83 | 84 | *registry = *foundRegistry.Payload 85 | } 86 | 87 | return nil 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /harbor/resource_replication_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 7 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 8 | ) 9 | 10 | func init() { 11 | resource.AddTestSweepers("resource_harbor_replication", &resource.Sweeper{ 12 | Name: "harbor_replication", 13 | }) 14 | } 15 | 16 | func TestAccHarborReplication_Basic(t *testing.T) { 17 | var project models.Project 18 | 19 | var registry models.Registry 20 | 21 | resource.Test(t, resource.TestCase{ 22 | PreCheck: func() { testAccHarborPreCheck(t) }, 23 | Providers: testAccProviders, 24 | Steps: []resource.TestStep{ 25 | { 26 | Config: testAccHarborCheckReplicationPullResourceConfig(), 27 | Check: resource.ComposeTestCheckFunc( 28 | testAccHarborCheckProjectExists("harbor_project.project_replica", &project), 29 | testAccHarborCheckRegistryExists("harbor_registry.registry_replica_helm_hub", ®istry), 30 | resource.TestCheckResourceAttr( 31 | "harbor_replication.pull_helm_chart", "name", "acc-helm-prometheus-operator-test"), 32 | resource.TestCheckResourceAttr( 33 | "harbor_replication.pull_helm_chart", "source_registry_filter_name", "stable/prometheus-operator"), 34 | resource.TestCheckResourceAttr( 35 | "harbor_replication.pull_helm_chart", "description", "Prometheus Operator Replica"), 36 | resource.TestCheckResourceAttr( 37 | "harbor_replication.pull_helm_chart", "destination_namespace", "acc-project-replica-test"), 38 | ), 39 | }, { 40 | Config: testAccHarborCheckReplicationPushResourceConfig(), 41 | Check: resource.ComposeTestCheckFunc( 42 | testAccHarborCheckRegistryExists("harbor_registry.registry_replica_push_helm_hub", ®istry), 43 | resource.TestCheckResourceAttr( 44 | "harbor_replication.push_helm_chart", "name", "acc-push-test"), 45 | resource.TestCheckResourceAttr( 46 | "harbor_replication.push_helm_chart", "source_registry_filter_name", "stable/prometheus-operator"), 47 | resource.TestCheckResourceAttr( 48 | "harbor_replication.push_helm_chart", "description", "Push Replica"), 49 | resource.TestCheckResourceAttr( 50 | "harbor_replication.push_helm_chart", "destination_namespace", "notexistingtest"), 51 | ), 52 | }, 53 | }, 54 | }) 55 | } 56 | 57 | func testAccHarborCheckReplicationPullResourceConfig() string { 58 | return ` 59 | resource "harbor_project" "project_replica" { 60 | name = "acc-project-replica-test" 61 | public = false 62 | vulnerability_scanning = false 63 | } 64 | resource "harbor_registry" "registry_replica_helm_hub" { 65 | name = "acc-registry-replica-test" 66 | url = "https://hub.helm.sh" 67 | type = "helm-hub" 68 | description = "Helm Hub Registry" 69 | insecure = false 70 | } 71 | resource "harbor_replication" "pull_helm_chart" { 72 | name = "acc-helm-prometheus-operator-test" 73 | description = "Prometheus Operator Replica" 74 | source_registry_id = harbor_registry.registry_replica_helm_hub.id 75 | source_registry_filter_name = "stable/prometheus-operator" 76 | source_registry_filter_tag = "**" 77 | destination_namespace = harbor_project.project_replica.name 78 | } 79 | ` 80 | } 81 | 82 | func testAccHarborCheckReplicationPushResourceConfig() string { 83 | return ` 84 | resource "harbor_registry" "registry_replica_push_helm_hub" { 85 | name = "acc-registry-push-replica-test" 86 | url = "https://hub.helm.sh" 87 | type = "helm-hub" 88 | description = "Helm Hub Registry" 89 | insecure = false 90 | } 91 | resource "harbor_label" "main_push" { 92 | name = "global-acc-push-golang" 93 | description = "Golang Acc Test Label" 94 | color = "#61717D" 95 | } 96 | resource "harbor_replication" "push_helm_chart" { 97 | name = "acc-push-test" 98 | description = "Push Replica" 99 | source_registry_filter_name = "stable/prometheus-operator" 100 | source_registry_filter_tag = "**" 101 | destination_registry_id = harbor_registry.registry_replica_push_helm_hub.id 102 | destination_namespace = "notexistingtest" 103 | } 104 | ` 105 | } 106 | -------------------------------------------------------------------------------- /harbor/resource_retention_policy.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 12 | ) 13 | 14 | func resourceRetentionPolicy() *schema.Resource { 15 | return &schema.Resource{ 16 | Schema: map[string]*schema.Schema{ 17 | "algorithm": { 18 | Type: schema.TypeString, 19 | Optional: true, 20 | Default: "or", 21 | }, 22 | "scope": { 23 | Type: schema.TypeList, 24 | Required: true, 25 | MaxItems: 1, 26 | Elem: &schema.Resource{ 27 | Schema: retentionPolicyScopeFields(), 28 | }, 29 | }, 30 | // is it optional? 0 item = null array? 31 | "rule": { 32 | Type: schema.TypeList, 33 | MinItems: 1, 34 | Required: true, 35 | Elem: &schema.Resource{ 36 | Schema: retentionPolicyRuleFields(), 37 | }, 38 | }, 39 | "trigger": { 40 | Type: schema.TypeList, 41 | MaxItems: 1, 42 | Required: true, 43 | Elem: &schema.Resource{ 44 | Schema: retentionPolicyTriggerFields(), 45 | }, 46 | }, 47 | }, 48 | Create: resourceRetentionPolicyCreate, 49 | Read: resourceRetentionPolicyRead, 50 | Update: resourceRetentionPolicyUpdate, 51 | Delete: resourceRetentionPolicyDelete, 52 | Importer: &schema.ResourceImporter{ 53 | State: schema.ImportStatePassthrough, 54 | }, 55 | } 56 | } 57 | 58 | func resourceRetentionPolicyCreate(d *schema.ResourceData, m interface{}) error { 59 | apiClient := m.(*client.Harbor) 60 | 61 | projectID := int64(d.Get("scope.0.ref").(int)) 62 | body := products.NewPostRetentionsParams().WithPolicy(&models.RetentionPolicy{ 63 | Algorithm: d.Get("algorithm").(string), 64 | Scope: &models.RetentionPolicyScope{ 65 | Level: d.Get("scope.0.level").(string), 66 | Ref: projectID, 67 | }, 68 | Rules: expandRetentionPolicyRules(d.Get("rule").([]interface{})), 69 | Trigger: &models.RetentionRuleTrigger{ 70 | Kind: d.Get("trigger.0.kind").(string), 71 | References: map[string]int{}, // default reference is empty map 72 | Settings: map[string]string{ 73 | "cron": d.Get("trigger.0.settings.0.cron").(string), 74 | }, 75 | }, 76 | }) 77 | 78 | if _, err := apiClient.Products.PostRetentions(body, nil); err != nil { 79 | return err 80 | } 81 | 82 | retentionID, err := findRetentionIDByProject(d, m, projectID) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | d.SetId(strconv.Itoa(int(retentionID))) 88 | 89 | return resourceRetentionPolicyRead(d, m) 90 | } 91 | 92 | func findRetentionIDByProject(d *schema.ResourceData, m interface{}, projectID int64) (int64, error) { 93 | apiClient := m.(*client.Harbor) 94 | 95 | query := products.NewGetProjectsProjectIDParams().WithProjectID(projectID) 96 | 97 | resp, err := apiClient.Products.GetProjectsProjectID(query, nil) 98 | if err != nil { 99 | d.SetId("") 100 | return 0, err 101 | } 102 | 103 | retentionID := resp.Payload.Metadata.RetentionID 104 | if retentionID == "" { 105 | return 0, fmt.Errorf("retention ID is null or empty for project ID %v", projectID) 106 | } 107 | 108 | retentionIDInt, err := strconv.Atoi(retentionID) 109 | if err != nil { 110 | return 0, fmt.Errorf("couldn't parse retention id %v from project ID %v: %v", retentionID, projectID, err) 111 | } 112 | 113 | return int64(retentionIDInt), nil 114 | } 115 | 116 | func resourceRetentionPolicyRead(d *schema.ResourceData, m interface{}) error { 117 | apiClient := m.(*client.Harbor) 118 | 119 | if retentionID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 120 | query := products.NewGetRetentionsIDParams().WithID(retentionID) 121 | 122 | resp, err := apiClient.Products.GetRetentionsID(query, nil) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | if err := setRetentionPolicySchema(d, resp.Payload); err != nil { 128 | return err 129 | } 130 | 131 | return nil 132 | } 133 | 134 | return fmt.Errorf("fail to load the retention policy") 135 | } 136 | 137 | func resourceRetentionPolicyUpdate(d *schema.ResourceData, m interface{}) error { 138 | apiClient := m.(*client.Harbor) 139 | 140 | if retentionID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 141 | body := products.NewPutRetentionsIDParams().WithPolicy(&models.RetentionPolicy{ 142 | Algorithm: d.Get("algorithm").(string), 143 | Scope: &models.RetentionPolicyScope{ 144 | Level: d.Get("scope.0.level").(string), 145 | Ref: int64(d.Get("scope.0.ref").(int)), 146 | }, 147 | Rules: expandRetentionPolicyRules(d.Get("rule").([]interface{})), 148 | Trigger: &models.RetentionRuleTrigger{ 149 | Kind: d.Get("trigger.0.kind").(string), 150 | References: expandRetentionPolicyTriggerReferencesUpdate(d.Get("trigger.0.references.0").(map[string]interface{})), 151 | Settings: map[string]string{ 152 | "cron": d.Get("trigger.0.settings.0.cron").(string), 153 | }, 154 | }, 155 | }).WithID(retentionID) 156 | 157 | if _, err := apiClient.Products.PutRetentionsID(body, nil); err != nil { 158 | return err 159 | } 160 | 161 | return resourceRetentionPolicyRead(d, m) 162 | } 163 | 164 | return fmt.Errorf("retention policy Id not a Integer") 165 | } 166 | 167 | // Delete is not supported by API 168 | // a good way to implement it would be: 169 | // - to empty the policy (but the id is still existent) 170 | // and - to use "put" for creation if empty policy exists 171 | // and - to prevent creation if non-empty pocity exists 172 | //func resourceRetentionPolicyDelete(d *schema.ResourceData, m interface{}) error { 173 | // apiClient := m.(*client.Harbor) 174 | // 175 | // if retentionID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 176 | // delete := products.NewDeleteRetentionsIDParams().WithID(retentionID) 177 | // if _, err := apiClient.Products.DeleteRetentionsID(delete, nil); err != nil { 178 | // return err 179 | // } 180 | // 181 | // return nil 182 | // } 183 | // 184 | // return fmt.Errorf("Retention policy Id not a Integer") 185 | //} 186 | 187 | func resourceRetentionPolicyDelete(d *schema.ResourceData, m interface{}) error { 188 | log.Print("not supported at the moment ... the problem is set empty values to the retention policy") 189 | return nil 190 | } 191 | 192 | func setRetentionPolicySchema(data *schema.ResourceData, policy *models.RetentionPolicy) error { 193 | data.SetId(strconv.Itoa(int(policy.ID))) 194 | 195 | if err := data.Set("algorithm", policy.Algorithm); err != nil { 196 | return err 197 | } 198 | 199 | scope, err := flattenRetentionPolicyScope(policy.Scope, data) 200 | if err != nil { 201 | return err 202 | } 203 | 204 | if err := data.Set("scope", scope); err != nil { 205 | return err 206 | } 207 | 208 | trigger, err := flattenRetentionPolicyTrigger(policy.Trigger, data) 209 | if err != nil { 210 | return err 211 | } 212 | 213 | if err := data.Set("trigger", trigger); err != nil { 214 | return err 215 | } 216 | 217 | rule, err := flattenRetentionPolicyRules(policy.Rules, data) 218 | if err != nil { 219 | return err 220 | } 221 | 222 | if err := data.Set("rule", rule); err != nil { 223 | return err 224 | } 225 | 226 | return nil 227 | } 228 | -------------------------------------------------------------------------------- /harbor/resource_robot_account.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 13 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 14 | ) 15 | 16 | func robotAccountNamePrefix() string { 17 | return "robot$" 18 | } 19 | 20 | func resourceRobotAccount() *schema.Resource { 21 | return &schema.Resource{ 22 | Schema: map[string]*schema.Schema{ 23 | "name": { 24 | Type: schema.TypeString, 25 | Required: true, 26 | ForceNew: true, 27 | }, 28 | "project_id": { 29 | Type: schema.TypeInt, 30 | Required: true, 31 | ForceNew: true, 32 | }, 33 | "description": { 34 | Type: schema.TypeString, 35 | Optional: true, 36 | Default: nil, 37 | ForceNew: true, 38 | }, 39 | "actions": { 40 | Type: schema.TypeList, 41 | Optional: true, 42 | ForceNew: true, 43 | Elem: &schema.Schema{Type: schema.TypeString}, 44 | }, 45 | "token": { 46 | Type: schema.TypeString, 47 | Computed: true, 48 | Sensitive: true, 49 | }, 50 | "disabled": { 51 | Type: schema.TypeBool, 52 | Computed: true, 53 | }, 54 | }, 55 | Create: resourceRobotAccountCreate, 56 | Read: resourceRobotAccountRead, 57 | Delete: resourceRobotAccountDelete, 58 | Importer: &schema.ResourceImporter{ 59 | State: schema.ImportStatePassthrough, 60 | }, 61 | } 62 | } 63 | 64 | func factoryRobotDockerEndpoint(projectID int64) string { 65 | return "/project/" + strconv.FormatInt(projectID, 10) + "/repository" 66 | } 67 | func factoryRobotAccountAccessDockerRead(projectID int64) *models.RobotAccountAccess { 68 | return &models.RobotAccountAccess{ 69 | Action: "pull", 70 | Resource: factoryRobotDockerEndpoint(projectID), 71 | } 72 | } 73 | func factoryRobotAccountAccessDockerPush(projectID int64) *models.RobotAccountAccess { 74 | return &models.RobotAccountAccess{ 75 | Action: "push", 76 | Resource: factoryRobotDockerEndpoint(projectID), 77 | } 78 | } 79 | 80 | func factoryRobotAccountAccessHelmChartPush(projectID int64) *models.RobotAccountAccess { 81 | return &models.RobotAccountAccess{ 82 | Action: "create", 83 | Resource: "/project/" + strconv.FormatInt(projectID, 10) + "/helm-chart-version", 84 | } 85 | } 86 | func factoryRobotAccountAccessHelmChartRead(projectID int64) *models.RobotAccountAccess { 87 | return &models.RobotAccountAccess{ 88 | Action: "read", 89 | Resource: "/project/" + strconv.FormatInt(projectID, 10) + "/helm-chart", 90 | } 91 | } 92 | 93 | func resourceRobotAccountCreate(d *schema.ResourceData, m interface{}) error { 94 | apiClient := m.(*client.Harbor) 95 | projectid := int64(d.Get("project_id").(int)) 96 | name := d.Get("name").(string) 97 | permissionsRoles := d.Get("actions").([]interface{}) 98 | // create the permission list for the robot account 99 | robotAccountAccess := make([]*models.RobotAccountAccess, len(permissionsRoles)) 100 | 101 | for i, role := range permissionsRoles { 102 | switch role.(string) { 103 | case "docker_read": 104 | robotAccountAccess[i] = (factoryRobotAccountAccessDockerRead(projectid)) 105 | case "docker_write": 106 | robotAccountAccess[i] = (factoryRobotAccountAccessDockerPush(projectid)) 107 | case "helm_read": 108 | robotAccountAccess[i] = (factoryRobotAccountAccessHelmChartRead(projectid)) 109 | case "helm_write": 110 | robotAccountAccess[i] = (factoryRobotAccountAccessHelmChartPush(projectid)) 111 | } 112 | } 113 | 114 | robotCreate := &models.RobotAccountCreate{ 115 | Name: name, 116 | Description: d.Get("description").(string), 117 | Access: robotAccountAccess, 118 | } 119 | 120 | params := products.NewPostProjectsProjectIDRobotsParams().WithProjectID(projectid).WithRobot(robotCreate) 121 | 122 | resp, err := apiClient.Products.PostProjectsProjectIDRobots(params, nil) 123 | 124 | if err != nil { 125 | return err 126 | } 127 | 128 | if err := d.Set("token", resp.Payload.Token); err != nil { 129 | return err 130 | } 131 | 132 | robot, err := findRobotAccountByProjectAndName(d, m) 133 | 134 | if err != nil { 135 | return err 136 | } 137 | 138 | d.SetId(strconv.Itoa(int(robot.ID))) 139 | 140 | return resourceRobotAccountRead(d, m) 141 | } 142 | 143 | func findRobotAccountByProjectAndName(d *schema.ResourceData, m interface{}) (*models.RobotAccount, error) { 144 | apiClient := m.(*client.Harbor) 145 | name, nameOk := d.GetOk("name") 146 | projectID, projectIDOk := d.GetOk("project_id") 147 | 148 | if !nameOk || !projectIDOk { 149 | return &models.RobotAccount{}, errors.New("fail to get the name and/or project_id for robot account request") 150 | } 151 | 152 | query := products.NewGetProjectsProjectIDRobotsParams().WithProjectID(int64(projectID.(int))) 153 | 154 | resp, err := apiClient.Products.GetProjectsProjectIDRobots(query, nil) 155 | 156 | if err != nil { 157 | return &models.RobotAccount{}, err 158 | } 159 | 160 | for _, v := range resp.Payload { 161 | if v.Name == "robot$"+name.(string) { 162 | return v, nil 163 | } 164 | } 165 | 166 | return &models.RobotAccount{}, 167 | fmt.Errorf("no Robot found for ProjectID %d, with Name %s", projectID.(int), name.(string)) 168 | } 169 | func resourceRobotAccountRead(d *schema.ResourceData, m interface{}) error { 170 | apiClient := m.(*client.Harbor) 171 | projectID := d.Get("project_id").(int) 172 | 173 | log.Printf("Load Robot Accounts from %v Project", projectID) 174 | 175 | if robotID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 176 | query := products.NewGetProjectsProjectIDRobotsRobotIDParams().WithProjectID(int64(projectID)).WithRobotID(robotID) 177 | 178 | resp, err := apiClient.Products.GetProjectsProjectIDRobotsRobotID(query, nil) 179 | if err != nil { 180 | return err 181 | } 182 | 183 | if err := setRobotSchema(d, resp.Payload); err != nil { 184 | return err 185 | } 186 | } 187 | 188 | return nil 189 | } 190 | func setRobotSchema(d *schema.ResourceData, model *models.RobotAccount) error { 191 | d.SetId(strconv.Itoa(int(model.ID))) 192 | 193 | if err := d.Set("name", strings.Replace(model.Name, robotAccountNamePrefix(), "", 1)); err != nil { 194 | return err 195 | } 196 | 197 | if err := d.Set("description", model.Description); err != nil { 198 | return err 199 | } 200 | 201 | if err := d.Set("project_id", int(model.ProjectID)); err != nil { 202 | return err 203 | } 204 | 205 | if err := d.Set("disabled", model.Disabled); err != nil { 206 | return err 207 | } 208 | 209 | return nil 210 | } 211 | 212 | func resourceRobotAccountDelete(d *schema.ResourceData, m interface{}) error { 213 | apiClient := m.(*client.Harbor) 214 | 215 | if robotID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 216 | projectID := int64(d.Get("project_id").(int)) 217 | params := products.NewDeleteProjectsProjectIDRobotsRobotIDParams().WithRobotID(robotID).WithProjectID(projectID) 218 | 219 | _, err := apiClient.Products.DeleteProjectsProjectIDRobotsRobotID(params, nil) 220 | if err != nil { 221 | return err 222 | } 223 | 224 | d.SetId("") 225 | 226 | return nil 227 | } 228 | 229 | return fmt.Errorf("fail to Remove Robot Account") 230 | } 231 | -------------------------------------------------------------------------------- /harbor/resource_robot_account_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 13 | ) 14 | 15 | func init() { 16 | resource.AddTestSweepers("resource_robot_account", &resource.Sweeper{ 17 | Name: "harbor_robot_account", 18 | }) 19 | } 20 | 21 | func TestAccHarborRobot_Basic(t *testing.T) { 22 | var robot models.RobotAccount 23 | 24 | var project models.Project 25 | 26 | resource.Test(t, resource.TestCase{ 27 | PreCheck: func() { testAccHarborPreCheck(t) }, 28 | Providers: testAccProviders, 29 | Steps: []resource.TestStep{ 30 | { 31 | Config: testAccHarborCheckRobotResourceConfig(), 32 | Check: resource.ComposeTestCheckFunc( 33 | testAccHarborCheckProjectExists("harbor_project.main", &project), 34 | testAccHarborCheckRobotExists("harbor_robot_account.master_robot", &robot), 35 | resource.TestCheckResourceAttr( 36 | "harbor_robot_account.master_robot", "name", "acc-robot-test"), 37 | resource.TestCheckResourceAttr( 38 | "harbor_robot_account.master_robot", "description", "Robot account used created by gloang acc"), 39 | testAccHarborCheckRobotHasGeneratedTokenExists( 40 | "harbor_robot_account.master_robot"), 41 | ), 42 | }, 43 | }, 44 | }) 45 | } 46 | 47 | func testAccHarborCheckRobotHasGeneratedTokenExists(n string) resource.TestCheckFunc { 48 | return func(s *terraform.State) error { 49 | rs, ok := s.RootModule().Resources[n] 50 | 51 | if !ok { 52 | return fmt.Errorf("Not found: %s", n) 53 | } 54 | 55 | if rs.Primary.ID == "" { 56 | return fmt.Errorf("No Record ID is set") 57 | } 58 | 59 | if rs.Primary.Attributes["token"] == "" { 60 | return fmt.Errorf("No token generated") 61 | } 62 | 63 | return nil 64 | } 65 | } 66 | 67 | func testAccHarborCheckRobotResourceConfig() string { 68 | return ` 69 | resource "harbor_project" "main" { 70 | name = "acc-robot-project" 71 | } 72 | 73 | resource "harbor_robot_account" "master_robot" { 74 | name = "acc-robot-test" 75 | description = "Robot account used created by gloang acc" 76 | project_id = harbor_project.main.id 77 | actions = ["docker_read", "docker_write", "helm_read", "helm_write"] 78 | } 79 | ` 80 | } 81 | 82 | func testAccHarborCheckRobotExists(n string, robot *models.RobotAccount) resource.TestCheckFunc { 83 | return func(s *terraform.State) error { 84 | rs, ok := s.RootModule().Resources[n] 85 | 86 | if !ok { 87 | return fmt.Errorf("Not found: %s", n) 88 | } 89 | 90 | if rs.Primary.ID == "" { 91 | return fmt.Errorf("No Record ID is set") 92 | } 93 | 94 | client := testAccProvider.Meta().(*client.Harbor) 95 | 96 | if searchID, err := strconv.ParseInt(rs.Primary.ID, 10, 64); err == nil { 97 | if projectID, err := strconv.ParseInt(rs.Primary.Attributes["project_id"], 10, 64); err == nil { 98 | query := products.NewGetProjectsProjectIDRobotsRobotIDParams().WithProjectID(projectID).WithRobotID(searchID) 99 | 100 | foundRobot, err := client.Products.GetProjectsProjectIDRobotsRobotID(query, nil) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | if foundRobot == nil { 106 | return fmt.Errorf("Record not found") 107 | } 108 | 109 | *robot = *foundRobot.Payload 110 | } 111 | } 112 | 113 | return nil 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /harbor/resource_tasks.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "strings" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 13 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 14 | ) 15 | 16 | func resourceTasks() *schema.Resource { 17 | return &schema.Resource{ 18 | Schema: map[string]*schema.Schema{ 19 | "vulnerability_scan_policy": { 20 | Type: schema.TypeString, 21 | Required: true, 22 | }, 23 | }, 24 | Create: resourceTasksCreate, 25 | Read: resourceTasksRead, 26 | Update: resourceTasksUpdate, 27 | Delete: resourceTasksDelete, 28 | } 29 | } 30 | 31 | func resourceTasksCreate(d *schema.ResourceData, m interface{}) error { 32 | apiClient := m.(*client.Harbor) 33 | 34 | schedule, err := getSchedule(d.Get("vulnerability_scan_policy").(string)) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | body := &models.AdminJobSchedule{ 40 | Schedule: schedule, 41 | } 42 | 43 | resp, err := apiClient.Products.GetSystemScanAllSchedule(products.NewGetSystemScanAllScheduleParams(), nil) 44 | 45 | if err != nil { 46 | log.Fatalf("Fail to load vulnerability_scan %v", err) 47 | } 48 | 49 | time := resp.Payload.Schedule.Type 50 | if time != "" { 51 | log.Printf("Shedule found performing PUT request") 52 | 53 | params := products.NewPutSystemScanAllScheduleParams().WithSchedule(body) 54 | 55 | _, err = apiClient.Products.PutSystemScanAllSchedule(params, nil) 56 | 57 | if err != nil { 58 | log.Fatalf("Fail to update vulnerability_scan %v", err) 59 | } 60 | } else { 61 | log.Printf("No shedule found performing POST request") 62 | 63 | params := products.NewPostSystemScanAllScheduleParams().WithSchedule(body) 64 | 65 | _, err = apiClient.Products.PostSystemScanAllSchedule(params, nil) 66 | 67 | if err != nil { 68 | log.Fatalf("Fail to create new vulnerability_scan %v", err) 69 | } 70 | } 71 | 72 | d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", "vulnerability_scan"))) 73 | 74 | return resourceTasksRead(d, m) 75 | } 76 | 77 | func resourceTasksRead(d *schema.ResourceData, m interface{}) error { 78 | apiClient := m.(*client.Harbor) 79 | 80 | resp, err := apiClient.Products.GetSystemScanAllSchedule(products.NewGetSystemScanAllScheduleParams(), nil) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | if err := d.Set("vulnerability_scan_policy", strings.ToLower(resp.Payload.Schedule.Type)); err != nil { 86 | return err 87 | } 88 | 89 | return nil 90 | } 91 | 92 | func resourceTasksUpdate(d *schema.ResourceData, m interface{}) error { 93 | apiClient := m.(*client.Harbor) 94 | 95 | schedule, err := getSchedule(d.Get("vulnerability_scan_policy").(string)) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | body := &models.AdminJobSchedule{ 101 | Schedule: schedule, 102 | } 103 | 104 | query := products.NewPutSystemScanAllScheduleParams().WithSchedule(body) 105 | 106 | _, err = apiClient.Products.PutSystemScanAllSchedule(query, nil) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | return resourceTasksRead(d, m) 112 | } 113 | 114 | func resourceTasksDelete(d *schema.ResourceData, m interface{}) error { 115 | // https://github.com/goharbor/harbor/issues/11083 116 | log.Printf("Not inplemented at the moment look gh issue: %s", "https://github.com/goharbor/harbor/issues/11083") 117 | return nil 118 | } 119 | 120 | func getSchedule(schedule string) (*models.AdminJobScheduleObj, error) { 121 | switch schedule { 122 | case "hourly": 123 | return &models.AdminJobScheduleObj{ 124 | Cron: "0 0 * * * *", 125 | Type: "Hourly", 126 | }, nil 127 | case "daily": 128 | return &models.AdminJobScheduleObj{ 129 | Cron: "0 0 0 * * *", 130 | Type: "Daily", 131 | }, nil 132 | case "weekly": 133 | return &models.AdminJobScheduleObj{ 134 | Cron: "0 0 0 * * 0", 135 | Type: "Weekly", 136 | }, nil 137 | } 138 | 139 | return &models.AdminJobScheduleObj{}, errors.New("Not a Valid schedule name %s" + schedule) 140 | } 141 | -------------------------------------------------------------------------------- /harbor/resource_usergroup.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 8 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 9 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 11 | ) 12 | 13 | var groupTypeName2Num = map[string]int64{ 14 | "ldap": 1, 15 | "http": 2, 16 | "oidc": 3, 17 | } 18 | 19 | var groupTypeNum2Name = map[int64]string{ 20 | 1: "ldap", 21 | 2: "http", 22 | 3: "oidc", 23 | } 24 | 25 | func resourceUsergroup() *schema.Resource { 26 | return &schema.Resource{ 27 | Schema: map[string]*schema.Schema{ 28 | "name": { 29 | Type: schema.TypeString, 30 | Required: true, 31 | ForceNew: true, 32 | }, 33 | "ldap_dn": { 34 | Type: schema.TypeString, 35 | Optional: true, 36 | Default: "", 37 | }, 38 | "type": { 39 | Type: schema.TypeString, 40 | Required: true, 41 | }, 42 | }, 43 | Create: resourceUsergroupCreate, 44 | Read: resourceUsergroupRead, 45 | Update: resourceUsergroupUpdate, 46 | Delete: resourceUsergroupDelete, 47 | Importer: &schema.ResourceImporter{ 48 | State: schema.ImportStatePassthrough, 49 | }, 50 | } 51 | } 52 | 53 | func getGroupTypeName(name string) (int64, error) { 54 | if num, ok := groupTypeName2Num[name]; ok { 55 | return num, nil 56 | } 57 | return 0, fmt.Errorf("group type \"%s\" is unknown", name) 58 | } 59 | 60 | func resourceUsergroupCreate(d *schema.ResourceData, m interface{}) error { 61 | apiClient := m.(*client.Harbor) 62 | usergroupTypeNum, err := getGroupTypeName(d.Get("type").(string)) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | body := products.NewPostUsergroupsParams().WithUsergroup(&models.UserGroup{ 68 | GroupType: usergroupTypeNum, 69 | LdapGroupDn: d.Get("ldap_dn").(string), 70 | GroupName: d.Get("name").(string), 71 | }) 72 | 73 | if _, err := apiClient.Products.PostUsergroups(body, nil); err != nil { 74 | return err 75 | } 76 | 77 | usergroup, err := findUsergroupByName(d, m) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | d.SetId(strconv.Itoa(int(usergroup.ID))) 83 | 84 | return resourceUsergroupRead(d, m) 85 | } 86 | 87 | func findUsergroupByName(d *schema.ResourceData, m interface{}) (*models.UserGroup, error) { 88 | apiClient := m.(*client.Harbor) 89 | 90 | if usergroupName, ok := d.GetOk("name"); ok { 91 | query := products.NewGetUsergroupsParams() 92 | 93 | resp, err := apiClient.Products.GetUsergroups(query, nil) 94 | if err != nil { 95 | d.SetId("") 96 | return &models.UserGroup{}, err 97 | } 98 | 99 | for _, usergroup := range resp.Payload { 100 | if usergroup.GroupName == usergroupName { 101 | return usergroup, nil 102 | } 103 | } 104 | 105 | return &models.UserGroup{}, fmt.Errorf("no usergroups with name %v", usergroupName) 106 | } 107 | 108 | return &models.UserGroup{}, fmt.Errorf("fail to lookup usergroup by Name") 109 | } 110 | 111 | func resourceUsergroupRead(d *schema.ResourceData, m interface{}) error { 112 | apiClient := m.(*client.Harbor) 113 | 114 | if usergroupID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 115 | query := products.NewGetUsergroupsGroupIDParams().WithGroupID(usergroupID) 116 | 117 | resp, err := apiClient.Products.GetUsergroupsGroupID(query, nil) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | if err := setUsergroupSchema(d, resp.Payload); err != nil { 123 | return err 124 | } 125 | 126 | return nil 127 | } 128 | 129 | return fmt.Errorf("fail to load the project") 130 | } 131 | 132 | func resourceUsergroupUpdate(d *schema.ResourceData, m interface{}) error { 133 | apiClient := m.(*client.Harbor) 134 | 135 | usergroupTypeNum, err := getGroupTypeName(d.Get("type").(string)) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | if usergroupID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 141 | body := products.NewPutUsergroupsGroupIDParams().WithUsergroup(&models.UserGroup{ 142 | GroupName: d.Get("name").(string), 143 | LdapGroupDn: d.Get("ldap_dn").(string), 144 | GroupType: usergroupTypeNum, 145 | }).WithGroupID(usergroupID) 146 | 147 | if _, err := apiClient.Products.PutUsergroupsGroupID(body, nil); err != nil { 148 | return err 149 | } 150 | 151 | return resourceUsergroupRead(d, m) 152 | } 153 | 154 | return fmt.Errorf("Usergroup Id not a Integer") 155 | } 156 | 157 | func resourceUsergroupDelete(d *schema.ResourceData, m interface{}) error { 158 | apiClient := m.(*client.Harbor) 159 | 160 | if usergroupID, err := strconv.ParseInt(d.Id(), 10, 64); err == nil { 161 | delete := products.NewDeleteUsergroupsGroupIDParams().WithGroupID(usergroupID) 162 | if _, err := apiClient.Products.DeleteUsergroupsGroupID(delete, nil); err != nil { 163 | return err 164 | } 165 | 166 | return nil 167 | } 168 | 169 | return fmt.Errorf("Usergroup Id not a Integer") 170 | } 171 | 172 | func setUsergroupSchema(data *schema.ResourceData, usergroup *models.UserGroup) error { 173 | data.SetId(strconv.Itoa(int(usergroup.ID))) 174 | 175 | if err := data.Set("name", usergroup.GroupName); err != nil { 176 | return err 177 | } 178 | 179 | if err := data.Set("ldap_dn", usergroup.LdapGroupDn); err != nil { 180 | return err 181 | } 182 | 183 | usergroupTypeName := groupTypeNum2Name[usergroup.GroupType] 184 | if err := data.Set("type", usergroupTypeName); err != nil { 185 | return err 186 | } 187 | 188 | return nil 189 | } 190 | -------------------------------------------------------------------------------- /harbor/resource_usergroup_test.go: -------------------------------------------------------------------------------- 1 | package harbor_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 10 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client" 11 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/client/products" 12 | "github.com/nolte/terraform-provider-harbor/gen/harborctl/models" 13 | ) 14 | 15 | func init() { 16 | resource.AddTestSweepers("resource_harbor_usergroup", &resource.Sweeper{ 17 | Name: "harbor_usergroup", 18 | }) 19 | } 20 | 21 | func TestAccHarborUsergroup_Basic(t *testing.T) { 22 | var usergroup models.UserGroup 23 | 24 | resource.Test(t, resource.TestCase{ 25 | PreCheck: func() { testAccHarborPreCheck(t) }, 26 | Providers: testAccProviders, 27 | Steps: []resource.TestStep{ 28 | { 29 | Config: testAccHarborCheckUsergroupResourceConfig(), 30 | Check: resource.ComposeTestCheckFunc( 31 | testAccHarborCheckUsergroupExists("harbor_usergroup.main", &usergroup), 32 | resource.TestCheckResourceAttr( 33 | "harbor_usergroup.main", "name", "acc-usergroup-test"), 34 | resource.TestCheckResourceAttr( 35 | "harbor_usergroup.main", "ldap_dn", ""), 36 | resource.TestCheckResourceAttr( 37 | "harbor_usergroup.main", "type", "http"), 38 | ), 39 | }, 40 | // Can't run the Ldap test without a LDAP server configured. 41 | // { 42 | // Config: testAccHarborCheckUsergroupLdapResourceConfig(), 43 | // Check: resource.ComposeTestCheckFunc( 44 | // testAccHarborCheckUsergroupExists("harbor_usergroup.main", &usergroup), 45 | // resource.TestCheckResourceAttr( 46 | // "harbor_usergroup.main", "name", "acc-usergroup-test-2"), 47 | // resource.TestCheckResourceAttr( 48 | // "harbor_usergroup.main", "ldap_dn", "cn=developers,ou=groups,dc=example,dc=com"), 49 | // resource.TestCheckResourceAttr( 50 | // "harbor_usergroup.main", "type", "http"), 51 | // ), 52 | // }, 53 | }, 54 | }) 55 | } 56 | 57 | func testAccHarborCheckUsergroupResourceConfig() string { 58 | return ` 59 | resource "harbor_usergroup" "main" { 60 | name = "acc-usergroup-test" 61 | type = "http" 62 | } 63 | ` 64 | } 65 | 66 | // Can't test LDAP type without an LDAP server or mock 67 | func testAccHarborCheckUsergroupLdapResourceConfig() string { 68 | return ` 69 | resource "harbor_usergroup" "main" { 70 | name = "acc-usergroup-test" 71 | ldap_dn = "cn=developers,ou=groups,dc=example,dc=com" 72 | type = "ldap" 73 | } 74 | ` 75 | } 76 | 77 | func testAccHarborCheckUsergroupExists(n string, usergroup *models.UserGroup) resource.TestCheckFunc { 78 | return func(s *terraform.State) error { 79 | rs, ok := s.RootModule().Resources[n] 80 | 81 | if !ok { 82 | return fmt.Errorf("Not found: %s", n) 83 | } 84 | 85 | if rs.Primary.ID == "" { 86 | return fmt.Errorf("No Record ID is set") 87 | } 88 | 89 | client := testAccProvider.Meta().(*client.Harbor) 90 | 91 | if searchID, err := strconv.ParseInt(rs.Primary.ID, 10, 64); err == nil { 92 | params := products.NewGetUsergroupsGroupIDParams().WithGroupID(searchID) 93 | 94 | foundUsergroup, err := client.Products.GetUsergroupsGroupID(params, nil) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | if foundUsergroup == nil { 100 | return fmt.Errorf("Record not found") 101 | } 102 | 103 | *usergroup = *foundUsergroup.Payload 104 | } 105 | 106 | return nil 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /harbor/schema_retention_policy.go: -------------------------------------------------------------------------------- 1 | package harbor 2 | 3 | import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 4 | 5 | func retentionPolicyRuleFields() map[string]*schema.Schema { 6 | s := map[string]*schema.Schema{ 7 | "id": { 8 | Type: schema.TypeInt, 9 | Computed: true, 10 | }, 11 | "priority": { 12 | Type: schema.TypeInt, 13 | Computed: true, 14 | }, 15 | "disabled": { 16 | Type: schema.TypeBool, 17 | Optional: true, 18 | Default: false, 19 | }, 20 | "action": { 21 | Type: schema.TypeString, 22 | Computed: true, 23 | }, 24 | "template": { 25 | Type: schema.TypeString, 26 | Required: true, 27 | }, 28 | "params": { 29 | Type: schema.TypeMap, 30 | Elem: &schema.Schema{ 31 | Type: schema.TypeInt, 32 | }, 33 | Optional: true, 34 | Default: map[string]int{}, 35 | }, 36 | "tag_selectors": { 37 | Type: schema.TypeList, 38 | Required: true, 39 | MaxItems: 1, 40 | Elem: &schema.Resource{ 41 | Schema: retentionPolicyRuleRetentionSelectorsFields(), 42 | }, 43 | }, 44 | "scope_selectors": { 45 | Type: schema.TypeList, 46 | Required: true, 47 | MaxItems: 1, 48 | Elem: &schema.Resource{ 49 | Schema: retentionPolicyRuleScopeSelectorsFields(), 50 | }, 51 | }, 52 | } 53 | 54 | return s 55 | } 56 | 57 | func retentionPolicyRuleScopeSelectorsFields() map[string]*schema.Schema { 58 | s := map[string]*schema.Schema{ 59 | "repository": { 60 | Type: schema.TypeList, 61 | Required: true, 62 | MaxItems: 1, 63 | Elem: &schema.Resource{ 64 | Schema: retentionPolicyRuleRetentionSelectorsFields(), 65 | }, 66 | }, 67 | } 68 | 69 | return s 70 | } 71 | 72 | func retentionPolicyRuleRetentionSelectorsFields() map[string]*schema.Schema { 73 | s := map[string]*schema.Schema{ 74 | "kind": { 75 | Type: schema.TypeString, 76 | Optional: true, 77 | Default: "doublestar", 78 | }, 79 | "extras": { 80 | Type: schema.TypeString, 81 | Optional: true, 82 | Default: "", 83 | }, 84 | "decoration": { 85 | Type: schema.TypeString, 86 | Required: true, 87 | }, 88 | "pattern": { 89 | Type: schema.TypeString, 90 | Required: true, 91 | }, 92 | } 93 | 94 | return s 95 | } 96 | 97 | func retentionPolicyScopeFields() map[string]*schema.Schema { 98 | s := map[string]*schema.Schema{ 99 | "level": { 100 | Type: schema.TypeString, 101 | Optional: true, 102 | Default: "project", 103 | }, 104 | "ref": { 105 | Type: schema.TypeInt, 106 | Required: true, 107 | }, 108 | } 109 | 110 | return s 111 | } 112 | 113 | func retentionPolicyTriggerSettingsFields() map[string]*schema.Schema { 114 | s := map[string]*schema.Schema{ 115 | "cron": { 116 | Type: schema.TypeString, 117 | Optional: true, 118 | Default: "", 119 | }, 120 | } 121 | 122 | return s 123 | } 124 | 125 | func retentionPolicyTriggerReferencesFields() map[string]*schema.Schema { 126 | s := map[string]*schema.Schema{ 127 | "job_id": { 128 | Type: schema.TypeInt, 129 | Computed: true, 130 | }, 131 | } 132 | 133 | return s 134 | } 135 | 136 | func retentionPolicyTriggerFields() map[string]*schema.Schema { 137 | s := map[string]*schema.Schema{ 138 | "kind": { 139 | Type: schema.TypeString, 140 | Optional: true, 141 | Default: "Schedule", 142 | }, 143 | "references": { 144 | Type: schema.TypeList, 145 | MaxItems: 1, 146 | Optional: true, 147 | Elem: &schema.Resource{ 148 | Schema: retentionPolicyTriggerReferencesFields(), 149 | }, 150 | Computed: true, 151 | }, 152 | "settings": { 153 | Type: schema.TypeList, 154 | MaxItems: 1, 155 | Required: true, 156 | Elem: &schema.Resource{ 157 | Schema: retentionPolicyTriggerSettingsFields(), 158 | }, 159 | }, 160 | } 161 | 162 | return s 163 | } 164 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/plugin" 5 | "github.com/nolte/terraform-provider-harbor/harbor" 6 | ) 7 | 8 | func main() { 9 | plugin.Serve(&plugin.ServeOpts{ 10 | ProviderFunc: harbor.Provider}) 11 | } 12 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: "Terraform Provider: Harbor" 2 | site_url: https://nolte.github.io/terraform-provider-harbor/ 3 | repo_url: https://github.com/nolte/terraform-provider-harbor 4 | docs_dir: documentation/mkdocs 5 | nav: 6 | - Getting Started: index.md 7 | - Guides: 8 | - Development: guides/development.md 9 | - Local E2E Tests: guides/kind.md 10 | - Provider: 11 | - Data Sources: 12 | - harbor_label: gen/data-sources/harbor_label.md 13 | - harbor_project: gen/data-sources/harbor_project.md 14 | - harbor_registry: gen/data-sources/harbor_registry.md 15 | - Resources: 16 | - harbor_config_system: gen/resources/harbor_config_system.md 17 | - harbor_configuration: gen/resources/harbor_configuration.md 18 | - harbor_label: gen/resources/harbor_label.md 19 | - harbor_project_member: gen/resources/harbor_project_member.md 20 | - harbor_project: gen/resources/harbor_project.md 21 | - harbor_registry: gen/resources/harbor_registry.md 22 | - harbor_replication: gen/resources/harbor_replication.md 23 | - harbor_retention_policy: gen/resources/harbor_retention_policy.md 24 | - harbor_robot_account: gen/resources/harbor_robot_account.md 25 | - harbor_tasks: gen/resources/harbor_tasks.md 26 | - harbor_usergroup: gen/resources/harbor_usergroup.md 27 | 28 | theme: readthedocs 29 | markdown_extensions: 30 | - toc: 31 | permalink: True 32 | - pymdownx.snippets: 33 | base_path: . 34 | - pymdownx.arithmatex 35 | - pymdownx.betterem: 36 | smart_enable: all 37 | - pymdownx.caret 38 | - pymdownx.critic 39 | - pymdownx.details 40 | - pymdownx.emoji: 41 | emoji_generator: !!python/name:pymdownx.emoji.to_svg 42 | - pymdownx.inlinehilite 43 | - pymdownx.magiclink 44 | - pymdownx.mark 45 | - pymdownx.smartsymbols 46 | - pymdownx.superfences 47 | - pymdownx.tasklist: 48 | custom_checkbox: true 49 | - pymdownx.tilde 50 | - pymdownx.highlight 51 | extra_css: 52 | - css/table.css 53 | - css/extras.css 54 | extra_javascript: 55 | # fix for https://github.com/mkdocs/mkdocs/issues/2028 56 | - js/table.js 57 | -------------------------------------------------------------------------------- /scripts/build-04-go-errorchecks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "==> Checking for unchecked errors..." 4 | 5 | # shellcheck disable=SC2230 6 | if ! which errcheck > /dev/null; then 7 | cd ./tools || exit 8 | echo "==> Installing errcheck..." 9 | go get github.com/kisielk/errcheck 10 | cd .. || exit 11 | fi 12 | 13 | # shellcheck disable=SC1126,SC2046 14 | err_files=$(errcheck -ignoretests \ 15 | -ignore 'github.com/hashicorp/terraform/helper/schema:Set' \ 16 | -ignore 'bytes:.*' \ 17 | -ignore 'io:Close|Write' \ 18 | $(go list ./...| grep -v /vendor/)) 19 | 20 | if [[ -n "${err_files}" ]]; then 21 | echo 'Unchecked errors found in the following places:' 22 | echo "${err_files}" 23 | echo "Please handle returned errors. You can check directly with \`make errcheck\`" 24 | exit 1 25 | fi 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /scripts/swagger-specs/patch.1.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "op": "add", 3 | "path": "/definitions/ConfigurationsResponse/properties/oidc_scope", 4 | "value": { 5 | "description": "The Scope of the OIDC.", 6 | "$ref": "#/definitions/StringConfigItem" 7 | } 8 | }, 9 | { 10 | "op": "add", 11 | "path": "/definitions/ConfigurationsResponse/properties/oidc_client_secret", 12 | "value": { 13 | "description": "The client Secret of the OIDC.", 14 | "$ref": "#/definitions/StringConfigItem" 15 | } 16 | }, 17 | { 18 | "op": "add", 19 | "path": "/definitions/ConfigurationsResponse/properties/oidc_groups_claim", 20 | "value": { 21 | "description": "The scope sent to OIDC server during authentication, should be separated by comma. It has to contain \u201copenid\u201d, and \u201coffline_access\u201d. If you are using google, please remove \u201coffline_access\u201d from this field.", 22 | "$ref": "#/definitions/StringConfigItem" 23 | } 24 | }, 25 | { 26 | "op": "add", 27 | "path": "/definitions/ConfigurationsResponse/properties/ldap_search_dn/$ref", 28 | "value": "#/definitions/StringConfigItem" 29 | }, 30 | { 31 | "op": "remove", 32 | "path": "/definitions/ConfigurationsResponse/properties/ldap_search_dn/type" 33 | }, 34 | { 35 | "op": "add", 36 | "path": "/definitions/ProjectMetadata/properties/retention_id", 37 | "value": { 38 | "type": "string", 39 | "description": "Retention policy id associated to the project if any" 40 | } 41 | }, 42 | { 43 | "op": "add", 44 | "path": "/definitions/Configurations/properties/email_password", 45 | "value": { 46 | "type": "string", 47 | "description": "The password for authenticate against SMTP server." 48 | } 49 | }, 50 | { 51 | "op": "add", 52 | "path": "/definitions/Configurations/properties/oidc_groups_claim", 53 | "value": { 54 | "type": "string", 55 | "description": "The client Scope Claim of the OIDC." 56 | } 57 | }, 58 | { 59 | "op": "add", 60 | "path": "/definitions/ConfigurationsResponse/properties/ldap_scope/$ref", 61 | "value": "#/definitions/IntegerConfigItem" 62 | }, 63 | { 64 | "op": "remove", 65 | "path": "/definitions/ConfigurationsResponse/properties/ldap_scope/type" 66 | } 67 | ] -------------------------------------------------------------------------------- /scripts/tst-15-execute-classic-acc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This can be also run with `` 4 | # ./tst-15-execute-classic-acc "/api/v2.0" 5 | 6 | set -e 7 | set -o pipefail 8 | set -o nounset 9 | 10 | echo "==> Starting the acception tests..." 11 | 12 | export TF_ACC=1 13 | HARBOR_ENDPOINT="$(kubectl get Ingress tf-harbor-test-harbor-ingress -n harbor -ojson | jq '.spec.rules[].host' -r | grep harbor)" 14 | export HARBOR_ENDPOINT 15 | export HARBOR_USERNAME=admin 16 | HARBOR_PASSWORD="$(kubectl -n harbor get secret tf-harbor-test-harbor-core -ojson | jq '.data.HARBOR_ADMIN_PASSWORD' -r | base64 -d)" 17 | export HARBOR_PASSWORD 18 | export HARBOR_INSECURE="true" 19 | export HARBOR_BASEPATH=${1:-"/api"} 20 | #export TF_LOG=TRACE 21 | # shellcheck disable=SC2046 22 | CURRENTDIR=$(pwd) 23 | cd ./examples/test/ 24 | go test --tags=integration -timeout 20m -v 25 | cd "${CURRENTDIR}" 26 | -------------------------------------------------------------------------------- /scripts/tst-15-execute-go-acc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This can be also run with `` 4 | # ./tst-01-prepare-harbor.sh "10-42-0-100.sslip.io" "1.2.0" 5 | 6 | set -e 7 | set -o pipefail 8 | set -o nounset 9 | 10 | echo "==> Starting the acception tests..." 11 | 12 | export TF_ACC=1 13 | HARBOR_ENDPOINT="$(kubectl get Ingress tf-harbor-test-harbor-ingress -n harbor -ojson | jq '.spec.rules[].host' -r | grep harbor)" 14 | export HARBOR_ENDPOINT 15 | export HARBOR_USERNAME=admin 16 | HARBOR_PASSWORD="$(kubectl -n harbor get secret tf-harbor-test-harbor-core -ojson | jq '.data.HARBOR_ADMIN_PASSWORD' -r | base64 -d)" 17 | export HARBOR_PASSWORD 18 | export HARBOR_INSECURE="true" 19 | export HARBOR_BASEPATH=${1:-"/api"} 20 | #export TF_LOG=TRACE 21 | # shellcheck disable=SC2046 22 | go test -timeout 20m $(go list ./... | grep -v 'vendor') -v 23 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/blang/semver/v4 v4.0.0 7 | github.com/evanphx/json-patch v4.2.0+incompatible 8 | github.com/go-swagger/go-swagger v0.25.0 9 | github.com/golangci/golangci-lint v1.30.0 10 | github.com/goreleaser/goreleaser v0.141.0 11 | github.com/jessevdk/go-flags v1.4.0 12 | github.com/magefile/mage v1.10.0 13 | github.com/nolte/plumbing v0.0.2-0.20200820161834-674f1ee627a1 14 | ) 15 | -------------------------------------------------------------------------------- /tools/mage.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/magefile/mage/mage" 9 | ) 10 | 11 | func main() { os.Exit(mage.Main()) } 12 | --------------------------------------------------------------------------------