├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE.md
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── release-drafter.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .goreleaser.yml
├── .vscode
└── launch.json
├── CHANGELOG.md
├── GNUmakefile
├── LICENSE
├── README.md
├── docker-compose.yaml
├── docs
├── data-sources
│ ├── netmaker_access_key.md
│ ├── netmaker_network.md
│ ├── netmaker_networks.md
│ └── netmaker_node.md
├── index.md
└── resources
│ ├── netmaker_access_key.md
│ ├── netmaker_egress.md
│ ├── netmaker_ingress.md
│ ├── netmaker_network.md
│ └── netmaker_user.md
├── examples
├── README.md
├── data-sources
│ └── netmaker_networks
│ │ └── data-source.tf
├── provider
│ └── provider.tf
└── resources
│ └── netmaker_network
│ └── resource.tf
├── go.mod
├── go.sum
├── helper
├── access_key.go
├── access_key_test.go
├── auth.go
├── auth_test.go
├── client.go
├── network.go
├── network_test.go
├── node.go
├── node_test.go
├── user.go
└── user_test.go
├── internal
└── provider
│ ├── data_source_access_key.go
│ ├── data_source_access_key_test.go
│ ├── data_source_network.go
│ ├── data_source_network_test.go
│ ├── data_source_networks.go
│ ├── data_source_networks_test.go
│ ├── data_source_node.go
│ ├── data_source_node_test.go
│ ├── provider.go
│ ├── provider_test.go
│ ├── resource_access_key.go
│ ├── resource_access_key_test.go
│ ├── resource_egress.go
│ ├── resource_egress_test.go
│ ├── resource_ingress.go
│ ├── resource_ingress_test.go
│ ├── resource_network.go
│ ├── resource_network_test.go
│ ├── resource_user.go
│ └── resource_user_test.go
├── main.go
└── tools
└── tools.go
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code.
4 |
5 | Please read the full text at https://www.hashicorp.com/community-guidelines
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Hi there,
2 |
3 | Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html.
4 |
5 | ### Terraform Version
6 | Run `terraform -v` to show the version. If you are not running the latest version of Terraform, please upgrade because your issue may have already been fixed.
7 |
8 | ### Affected Resource(s)
9 | Please list the resources as a list, for example:
10 | - opc_instance
11 | - opc_storage_volume
12 |
13 | If this issue appears to affect multiple resources, it may be an issue with Terraform's core, so please mention this.
14 |
15 | ### Terraform Configuration Files
16 | ```hcl
17 | # Copy-paste your Terraform configurations here - for large Terraform configs,
18 | # please use a service like Dropbox and share a link to the ZIP file. For
19 | # security, you can also encrypt the files using our GPG public key.
20 | ```
21 |
22 | ### Debug Output
23 | Please provider a link to a GitHub Gist containing the complete debug output: https://www.terraform.io/docs/internals/debugging.html. Please do NOT paste the debug output in the issue; just paste a link to the Gist.
24 |
25 | ### Panic Output
26 | If Terraform produced a panic, please provide a link to a GitHub Gist containing the output of the `crash.log`.
27 |
28 | ### Expected Behavior
29 | What should have happened?
30 |
31 | ### Actual Behavior
32 | What actually happened?
33 |
34 | ### Steps to Reproduce
35 | Please list the steps required to reproduce the issue, for example:
36 | 1. `terraform apply`
37 |
38 | ### Important Factoids
39 | Are there anything atypical about your accounts that we should know? For example: Running in EC2 Classic? Custom version of OpenStack? Tight ACLs?
40 |
41 | ### References
42 | Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example:
43 | - GH-1234
44 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # See GitHub's docs for more information on this file:
2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
3 | version: 2
4 | updates:
5 | # Maintain dependencies for GitHub Actions
6 | - package-ecosystem: "github-actions"
7 | directory: "/"
8 | schedule:
9 | # Check for updates to GitHub Actions every weekday
10 | interval: "daily"
11 |
12 | # Maintain dependencies for Go modules
13 | - package-ecosystem: "gomod"
14 | directory: "/"
15 | schedule:
16 | # Check for updates to Go modules every weekday
17 | interval: "daily"
18 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION 🌈'
2 | template: "## Changes\n $CHANGES"
3 | tag-template: 'v$RESOLVED_VERSION'
4 | categories:
5 | - title: '🚀 Features'
6 | labels:
7 | - 'feature'
8 | - 'enhancement'
9 | - title: '🐛 Bug Fixes'
10 | labels:
11 | - 'fix'
12 | - 'bugfix'
13 | - 'bug'
14 | - title: '🧰 Maintenance'
15 | label: 'chore'
16 | - title: '📗 Docs'
17 | label: 'docs'
18 | - title: '🥘 Dependencies Update'
19 | label: 'dependencies'
20 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
21 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
22 | version-resolver:
23 | major:
24 | labels:
25 | - 'major'
26 | minor:
27 | labels:
28 | - 'minor'
29 | - 'feature'
30 | patch:
31 | labels:
32 | - 'patch'
33 | default: patch
34 | autolabeler:
35 | - label: 'docs'
36 | files:
37 | - '*.md'
38 | branch:
39 | - '/docs\/.+/'
40 | - label: 'chore'
41 | branch:
42 | - '/chore\/.+/'
43 | - label: 'bug'
44 | branch:
45 | - '/fix\/.+/'
46 | title:
47 | - '/fix/i'
48 | - label: 'feature'
49 | branch:
50 | - '/feat\/.+/'
51 |
52 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '24 11 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'go' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v2
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v2
71 |
--------------------------------------------------------------------------------
/.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 | - master
8 | # pull_request event is required only for autolabeler
9 | pull_request:
10 | # Only following types are handled by the action, but one can default to all as well
11 | types: [opened, reopened, synchronize]
12 |
13 | jobs:
14 | update_release_draft:
15 | runs-on: ubuntu-latest
16 | steps:
17 | # Drafts your next Release notes as Pull Requests are merged into "master"
18 | - uses: release-drafter/release-drafter@v5
19 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
20 | # with:
21 | # config-name: my-config.yml
22 | # disable-autolabeler: true
23 | env:
24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This GitHub action can publish assets for release when a tag is created.
2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
3 | #
4 | # This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your
5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE`
6 | # secret. If you would rather own your own GPG handling, please fork this action
7 | # or use an alternative one for key handling.
8 | #
9 | # You will need to pass the `--batch` flag to `gpg` in your signing step
10 | # in `goreleaser` to indicate this is being used in a non-interactive mode.
11 | #
12 | name: release
13 | on:
14 | push:
15 | tags:
16 | - 'v*'
17 | jobs:
18 | goreleaser:
19 | runs-on: ubuntu-latest
20 | steps:
21 | -
22 | name: Checkout
23 | uses: actions/checkout@v3
24 | -
25 | name: Unshallow
26 | run: git fetch --prune --unshallow
27 | -
28 | name: Set up Go
29 | uses: actions/setup-go@v2
30 | with:
31 | go-version: 1.14
32 | -
33 | name: Import GPG key
34 | id: import_gpg
35 | uses: hashicorp/ghaction-import-gpg@v2.1.0
36 | env:
37 | # These secrets will need to be configured for the repository:
38 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
39 | PASSPHRASE: ${{ secrets.PASSPHRASE }}
40 | -
41 | name: Run GoReleaser
42 | uses: goreleaser/goreleaser-action@v2.8.0
43 | with:
44 | version: latest
45 | args: release --rm-dist
46 | env:
47 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
48 | # GitHub sets this automatically
49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # This GitHub action runs your tests for each commit push and/or PR. Optionally
2 | # you can turn it on using a cron schedule for regular testing.
3 | #
4 | name: Tests
5 | on:
6 | pull_request:
7 | paths-ignore:
8 | - 'README.md'
9 | push:
10 | paths-ignore:
11 | - 'README.md'
12 | # For systems with an upstream API that could drift unexpectedly (like most SaaS systems, etc.),
13 | # we recommend testing at a regular interval not necessarily tied to code changes. This will
14 | # ensure you are alerted to something breaking due to an API change, even if the code did not
15 | # change.
16 | # schedule:
17 | # - cron: '0 13 * * *'
18 | jobs:
19 | # ensure the code builds...
20 | build:
21 | name: Build
22 | runs-on: ubuntu-latest
23 | timeout-minutes: 5
24 | steps:
25 |
26 | - name: Set up Go
27 | uses: actions/setup-go@v2.1.3
28 | with:
29 | go-version: '1.15'
30 | id: go
31 |
32 | - name: Check out code into the Go module directory
33 | uses: actions/checkout@v3
34 |
35 | - name: Get dependencies
36 | run: |
37 | go mod download
38 |
39 | - name: Build
40 | run: |
41 | go build -v .
42 |
43 | # run acceptance tests in a matrix with Terraform core versions
44 | test:
45 | name: Matrix Test
46 | needs: build
47 | runs-on: ubuntu-latest
48 | timeout-minutes: 15
49 | strategy:
50 | fail-fast: false
51 | matrix:
52 | # list whatever Terraform versions here you would like to support
53 | terraform:
54 | - '0.12.29'
55 | - '0.13.4'
56 | - '0.14.0-beta2'
57 | steps:
58 |
59 | - name: Set up Go
60 | uses: actions/setup-go@v2.1.3
61 | with:
62 | go-version: '1.15'
63 | id: go
64 |
65 | - name: Check out code into the Go module directory
66 | uses: actions/checkout@v3
67 |
68 | - name: Get dependencies
69 | run: |
70 | go mod download
71 |
72 | - name: TF acceptance tests
73 | timeout-minutes: 10
74 | env:
75 | TF_ACC: "1"
76 | TF_ACC_TERRAFORM_VERSION: ${{ matrix.terraform }}
77 |
78 | # Set whatever additional acceptance test env vars here. You can
79 | # optionally use data from your repository secrets using the
80 | # following syntax:
81 | # SOME_VAR: ${{ secrets.SOME_VAR }}
82 |
83 | run: |
84 | go test -v -cover ./internal/provider/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.dll
2 | *.exe
3 | .DS_Store
4 | example.tf
5 | terraform.tfplan
6 | terraform.tfstate
7 | bin/
8 | dist/
9 | modules-dev/
10 | /pkg/
11 | website/.vagrant
12 | website/.bundle
13 | website/build
14 | website/node_modules
15 | .vagrant/
16 | *.backup
17 | ./*.tfstate
18 | .terraform/
19 | *.log
20 | *.bak
21 | *~
22 | .*.swp
23 | .idea
24 | *.iml
25 | *.test
26 | *.iml
27 | *.lock.hcl
28 | website/vendor
29 | vendor
30 |
31 | # Test exclusions
32 | !command/test-fixtures/**/*.tfstate
33 | !command/test-fixtures/**/.terraform/
34 |
35 | # Keep windows files with windows line endings
36 | *.winfile eol=crlf
37 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # Visit https://goreleaser.com for documentation on how to customize this
2 | # behavior.
3 | before:
4 | hooks:
5 | # this is just an example and not a requirement for provider building/publishing
6 | - go mod tidy
7 | builds:
8 | - env:
9 | # goreleaser does not work with CGO, it could also complicate
10 | # usage by users in CI/CD systems like Terraform Cloud where
11 | # they are unable to install libraries.
12 | - CGO_ENABLED=0
13 | mod_timestamp: '{{ .CommitTimestamp }}'
14 | flags:
15 | - -trimpath
16 | ldflags:
17 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
18 | goos:
19 | - freebsd
20 | - windows
21 | - linux
22 | - darwin
23 | goarch:
24 | - amd64
25 | - '386'
26 | - arm
27 | - arm64
28 | ignore:
29 | - goos: darwin
30 | goarch: '386'
31 | binary: '{{ .ProjectName }}_v{{ .Version }}'
32 | archives:
33 | - format: zip
34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
35 | checksum:
36 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
37 | algorithm: sha256
38 | signs:
39 | - artifacts: checksum
40 | args:
41 | # if you are using this in a GitHub action or some other automated pipeline, you
42 | # need to pass the batch flag to indicate its not interactive.
43 | - "--batch"
44 | - "--local-user"
45 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
46 | - "--output"
47 | - "${signature}"
48 | - "--detach-sign"
49 | - "${artifact}"
50 | release:
51 | # If you want to manually examine the release before its live, uncomment this line:
52 | # draft: true
53 | changelog:
54 | skip: true
55 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Acceptance Tests",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "test",
12 | // this assumes your workspace is the root of the repo
13 | "program": "${fileDirname}",
14 | "env": {
15 | "TF_ACC": "1",
16 | },
17 | "args": [],
18 | },
19 | {
20 | "name": "Debug - Attach External CLI",
21 | "type": "go",
22 | "request": "launch",
23 | "mode": "debug",
24 | // this assumes your workspace is the root of the repo
25 | "program": "${workspaceFolder}",
26 | "env": {},
27 | "args": [
28 | // pass the debug flag for reattaching
29 | "-debug",
30 | ],
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.1.0 (Unreleased)
2 |
3 | BACKWARDS INCOMPATIBILITIES / NOTES:
4 |
--------------------------------------------------------------------------------
/GNUmakefile:
--------------------------------------------------------------------------------
1 | TEST?=$$(go list ./... | grep -v 'vendor')
2 | HOSTNAME=github.com
3 | NAMESPACE=madacluster
4 | NAME=netmaker
5 | BINARY=terraform-provider-${NAME}
6 | VERSION=0.2
7 | OS_ARCH=darwin_amd64
8 |
9 | default: install
10 |
11 | build:
12 | go build -o ${BINARY}
13 |
14 | release:
15 | GOOS=darwin GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_darwin_amd64
16 | GOOS=freebsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_freebsd_386
17 | GOOS=freebsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_freebsd_amd64
18 | GOOS=freebsd GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_freebsd_arm
19 | GOOS=linux GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_linux_386
20 | GOOS=linux GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_linux_amd64
21 | GOOS=linux GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_linux_arm
22 | GOOS=openbsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_openbsd_386
23 | GOOS=openbsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_openbsd_amd64
24 | GOOS=solaris GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_solaris_amd64
25 | GOOS=windows GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_windows_386
26 | GOOS=windows GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_windows_amd64
27 |
28 | install: build
29 | mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
30 | mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
31 |
32 | test:
33 | go test -i $(TEST) || exit 1
34 | echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4
35 |
36 | testacc:
37 | TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform Provider Netmaker
2 | [](https://www.codefactor.io/repository/github/madacluster/netmaker-terraform-provider)
3 |
4 | This repository is a *template* for a [Terraform](https://www.terraform.io) provider. It is intended as a starting point for creating Terraform providers, containing:
5 |
6 | - A resource, and a data source (`internal/provider/`),
7 | - Examples (`examples/`) and generated documentation (`docs/`),
8 | - Miscellaneous meta files.
9 |
10 | These files contain boilerplate code that you will need to edit to create your own Terraform provider. A full guide to creating Terraform providers can be found at [Writing Custom Providers](https://www.terraform.io/docs/extend/writing-custom-providers.html).
11 |
12 | Please see the [GitHub template repository documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for how to create a new repository from this template on GitHub.
13 |
14 | Once you've written your provider, you'll want to [publish it on the Terraform Registry](https://www.terraform.io/docs/registry/providers/publishing.html) so that others can use it.
15 |
16 | ## Features
17 | **Networks API**
18 | - [x] Get All Networks: /api/networks, GET
19 |
20 | - [x] Create Network: /api/network, POST
21 |
22 | - [x] Get Network: /api/networks/{network id}, GET
23 |
24 | - [x] Update Network: /api/networks/{network id}, PUT
25 |
26 | - [x] Delete Network: /api/networks/{network id}, DELETE
27 |
28 | **Access Keys API**
29 |
30 | - [ ] Get All Keys: /api/networks/{network id}/keys, GET
31 |
32 | - [ ] Create Key: /api/networks/{network id}/keys, GET
33 |
34 | - [ ] Delete Key: /api/networks/{network id}/keys/{keyname}, DELETE
35 |
36 | **Nodes API**
37 |
38 | - [ ] Create a Gateway: /api/nodes/{network id}/{macaddress}/creategateway, POST
39 |
40 | - [ ] Delete a Gateway: /api/nodes/{network id}/{macaddress}/deletegateway, DELETE
41 |
42 | - [ ] Get Network Nodes: /api/nodes/{network id}, GET
43 |
44 | - [ ] Get Node: /api/nodes/{network id}/{macaddress}, GET
45 |
46 | - [ ] Update Node: /api/nodes/{network id}/{macaddress}, PUT
47 |
48 | - [ ] Delete Node: /api/nodes/{network id}/{macaddress}, DELETE
49 |
50 | **Users API**
51 | Note: Only able to create Admin user at this time. The "user" is only used by the user interface to authenticate the single admin user.
52 |
53 | - [ ] Get User: /api/users/{username}, GET
54 |
55 | - [ ] Update User: /api/users/{username}, PUT
56 |
57 | - [ ] Delete User: /api/users/{username}, DELETE
58 |
59 | - [ ] Check for Admin User: /api/users/adm/hasadmin, GET
60 |
61 | - [ ] Create Admin User: /api/users/adm/createadmin, POST
62 |
63 | - [x] Authenticate: /api/users/adm/authenticate, POST
64 |
65 |
66 | ## Requirements
67 |
68 | - [Terraform](https://www.terraform.io/downloads.html) >= 0.13.x
69 | - [Go](https://golang.org/doc/install) >= 1.15
70 |
71 | ## Building The Provider
72 |
73 | 1. Clone the repository
74 | 1. Enter the repository directory
75 | 1. Build the provider using the Go `install` command:
76 | ```sh
77 | $ go install
78 | ```
79 |
80 | ## Adding Dependencies
81 |
82 | This provider uses [Go modules](https://github.com/golang/go/wiki/Modules).
83 | Please see the Go documentation for the most up to date information about using Go modules.
84 |
85 | To add a new dependency `github.com/author/dependency` to your Terraform provider:
86 |
87 | ```
88 | go get github.com/author/dependency
89 | go mod tidy
90 | ```
91 |
92 | Then commit the changes to `go.mod` and `go.sum`.
93 |
94 | ## Using the provider
95 |
96 | Fill this in for each provider
97 |
98 | ## Developing the Provider
99 |
100 | If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above).
101 |
102 | To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory.
103 |
104 | To generate or update documentation, run `go generate`.
105 |
106 | In order to run the full suite of Acceptance tests, run `make testacc`.
107 |
108 | *Note:* Acceptance tests create real resources, and often cost money to run.
109 |
110 | ```sh
111 | $ make testacc
112 | ```
113 |
114 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: "3.4"
2 |
3 | services:
4 | netmaker:
5 | container_name: netmaker
6 | image: gravitl/netmaker:0.9.0-dev
7 | restart: always
8 | environment:
9 | DNS_MODE: "off"
10 | API_PORT: "8081"
11 | GRPC_PORT: "50051"
12 | CLIENT_MODE: "off"
13 | DISPLAY_KEYS: "on"
14 | MASTER_KEY: "REPLACE_MASTER_KEY"
15 | SERVER_GRPC_WIREGUARD: "off"
16 | CORS_ALLOWED_ORIGIN: "*"
17 | DATABASE: "sqlite"
18 | ports:
19 | - "8081:8081"
--------------------------------------------------------------------------------
/docs/data-sources/netmaker_access_key.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_access_key Data Source - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | AccessKey Data source in the Terraform provider Netmaker.
7 | ---
8 |
9 | # netmaker_access_key (Data Source)
10 |
11 | AccessKey Data source in the Terraform provider Netmaker.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **name** (String) Name of the access key
21 | - **netid** (String) Name of the access key
22 | - **uses** (Number) Uses of the access key
23 |
24 | ### Optional
25 |
26 | - **id** (String) The ID of this resource.
27 |
28 | ### Read-Only
29 |
30 | - **key** (String) Key of the access key
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs/data-sources/netmaker_network.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_network Data Source - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | models.Network Data source in the Terraform provider Netmaker.
7 | ---
8 |
9 | # netmaker_network (Data Source)
10 |
11 | models.Network Data source in the Terraform provider Netmaker.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **addressrange** (String)
21 | - **netid** (String)
22 |
23 | ### Optional
24 |
25 | - **id** (String) The ID of this resource.
26 |
27 | ### Read-Only
28 |
29 | - **addressrange6** (String)
30 | - **allowmanualsignup** (String)
31 | - **checkininterval** (Number)
32 | - **defaultextclientdns** (String)
33 | - **defaultinterface** (String)
34 | - **defaultkeepalive** (Number)
35 | - **defaultlistenport** (Number)
36 | - **defaultmtu** (Number)
37 | - **defaultpostdown** (String)
38 | - **defaultpostup** (String)
39 | - **defaultsaveconfig** (String)
40 | - **defaultudpholepunch** (String)
41 | - **displayname** (String)
42 | - **isdualstack** (String)
43 | - **isgrpchub** (String)
44 | - **isipv4** (String)
45 | - **isipv6** (String)
46 | - **islocal** (String)
47 | - **localrange** (String)
48 | - **nodelimit** (Number)
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/data-sources/netmaker_networks.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_networks Data Source - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | models.Network Data source in the Terraform provider Netmaker.
7 | ---
8 |
9 | # netmaker_networks (Data Source)
10 |
11 | models.Network Data source in the Terraform provider Netmaker.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | terraform {
17 | required_providers {
18 | netmaker = {
19 | version = "0.2"
20 | source = "github.com/madacluster/netmaker"
21 | }
22 | }
23 | }
24 |
25 | provider "netmaker" {
26 | username = "admin"
27 | password = "mx4S6JsSg7JWcZ"
28 | host = "http://localhost:8081"
29 | }
30 |
31 | data "netmaker_networks" "example" {
32 | # sample_attribute = "foo"
33 | }
34 |
35 | output "networks" {
36 | value = data.netmaker_networks.example.networks
37 | }
38 | ```
39 |
40 |
41 | ## Schema
42 |
43 | ### Optional
44 |
45 | - **id** (String) The ID of this resource.
46 |
47 | ### Read-Only
48 |
49 | - **networks** (List of Object) (see [below for nested schema](#nestedatt--networks))
50 |
51 |
52 | ### Nested Schema for `networks`
53 |
54 | Read-Only:
55 |
56 | - **addressrange** (String)
57 | - **addressrange6** (String)
58 | - **allowmanualsignup** (String)
59 | - **checkininterval** (Number)
60 | - **defaultextclientdns** (String)
61 | - **defaultinterface** (String)
62 | - **defaultkeepalive** (Number)
63 | - **defaultlistenport** (Number)
64 | - **defaultmtu** (Number)
65 | - **defaultpostdown** (String)
66 | - **defaultpostup** (String)
67 | - **defaultsaveconfig** (String)
68 | - **defaultudpholepunch** (String)
69 | - **displayname** (String)
70 | - **isdualstack** (String)
71 | - **isgrpchub** (String)
72 | - **isipv4** (String)
73 | - **isipv6** (String)
74 | - **islocal** (String)
75 | - **localrange** (String)
76 | - **netid** (String)
77 | - **nodelimit** (Number)
78 |
79 |
80 |
--------------------------------------------------------------------------------
/docs/data-sources/netmaker_node.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_node Data Source - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Node Data source in the Terraform provider Netmaker.
7 | ---
8 |
9 | # netmaker_node (Data Source)
10 |
11 | Node Data source in the Terraform provider Netmaker.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **mac** (String) The MAC address of the node
21 | - **network_id** (String) The ID of the network the node belongs to
22 |
23 | ### Optional
24 |
25 | - **id** (String) The ID of this resource.
26 | - **is_egress_gateway** (String) Is the node an egress gateway
27 | - **is_ingress_gateway** (String) Is the node an ingress gateway
28 | - **name** (String) The name of the node
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker-terraform-provider Provider"
4 | subcategory: ""
5 | description: |-
6 |
7 | ---
8 |
9 | # netmaker-terraform-provider Provider
10 |
11 |
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | provider "netmaker" {
17 | # example configuration here
18 | username = ""
19 | password = ""
20 | host = ""
21 | }
22 | ```
23 |
24 |
25 | ## Schema
26 |
27 | ### Required
28 |
29 | - **host** (String, Sensitive)
30 | - **password** (String, Sensitive)
31 | - **username** (String)
32 |
--------------------------------------------------------------------------------
/docs/resources/netmaker_access_key.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_access_key Resource - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Sample resource in the Terraform provider scaffolding.
7 | ---
8 |
9 | # netmaker_access_key (Resource)
10 |
11 | Sample resource in the Terraform provider scaffolding.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **name** (String) Name of the access key
21 | - **netid** (String) Name of the access key
22 | - **uses** (Number) Uses of the access key
23 |
24 | ### Optional
25 |
26 | - **id** (String) The ID of this resource.
27 | - **last_updated** (String)
28 |
29 | ### Read-Only
30 |
31 | - **key** (String) Key of the access key
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/resources/netmaker_egress.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_egress Resource - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Sample resource in the Terraform provider scaffolding.
7 | ---
8 |
9 | # netmaker_egress (Resource)
10 |
11 | Sample resource in the Terraform provider scaffolding.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **mac** (String) The MAC address of the node
21 | - **netid** (String) The ID of the network the node belongs to
22 |
23 | ### Optional
24 |
25 | - **id** (String) The ID of this resource.
26 | - **interface** (String) The interface the node is connected to
27 | - **last_updated** (String)
28 | - **ranges** (Set of String) The ranges the node is allowed to access
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/resources/netmaker_ingress.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_ingress Resource - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Sample resource in the Terraform provider scaffolding.
7 | ---
8 |
9 | # netmaker_ingress (Resource)
10 |
11 | Sample resource in the Terraform provider scaffolding.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **mac** (String) The MAC address of the node
21 | - **netid** (String) The ID of the network the node belongs to
22 |
23 | ### Optional
24 |
25 | - **id** (String) The ID of this resource.
26 | - **last_updated** (String)
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/resources/netmaker_network.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_network Resource - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Sample resource in the Terraform provider scaffolding.
7 | ---
8 |
9 | # netmaker_network (Resource)
10 |
11 | Sample resource in the Terraform provider scaffolding.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | resource "netmaker_network" "example" {
17 | sample_attribute = "foo"
18 | }
19 | ```
20 |
21 |
22 | ## Schema
23 |
24 | ### Required
25 |
26 | - **addressrange** (String)
27 | - **netid** (String)
28 |
29 | ### Optional
30 |
31 | - **id** (String) The ID of this resource.
32 | - **last_updated** (String)
33 |
34 | ### Read-Only
35 |
36 | - **addressrange6** (String)
37 | - **allowmanualsignup** (String)
38 | - **checkininterval** (Number)
39 | - **defaultextclientdns** (String)
40 | - **defaultinterface** (String)
41 | - **defaultkeepalive** (Number)
42 | - **defaultlistenport** (Number)
43 | - **defaultmtu** (Number)
44 | - **defaultpostdown** (String)
45 | - **defaultpostup** (String)
46 | - **defaultsaveconfig** (String)
47 | - **defaultudpholepunch** (String)
48 | - **displayname** (String)
49 | - **isdualstack** (String)
50 | - **isgrpchub** (String)
51 | - **isipv4** (String)
52 | - **isipv6** (String)
53 | - **islocal** (String)
54 | - **localrange** (String)
55 | - **nodelimit** (Number)
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/resources/netmaker_user.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "netmaker_user Resource - netmaker-terraform-provider"
4 | subcategory: ""
5 | description: |-
6 | Sample resource in the Terraform provider scaffolding.
7 | ---
8 |
9 | # netmaker_user (Resource)
10 |
11 | Sample resource in the Terraform provider scaffolding.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **username** (String)
21 |
22 | ### Optional
23 |
24 | - **id** (String) The ID of this resource.
25 | - **last_updated** (String)
26 | - **networks** (List of String)
27 | - **password** (String, Sensitive)
28 |
29 |
30 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI.
4 |
5 | The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation.
6 |
7 | * **provider/provider.tf** example file for the provider index page
8 | * **data-sources//data-source.tf** example file for the named data source page
9 | * **resources//resource.tf** example file for the named data source page
10 |
--------------------------------------------------------------------------------
/examples/data-sources/netmaker_networks/data-source.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | netmaker = {
4 | version = "0.2"
5 | source = "github.com/madacluster/netmaker"
6 | }
7 | }
8 | }
9 |
10 | provider "netmaker" {
11 | username = "admin"
12 | password = "mx4S6JsSg7JWcZ"
13 | host = "http://localhost:8081"
14 | }
15 |
16 | data "netmaker_networks" "example" {
17 | # sample_attribute = "foo"
18 | }
19 |
20 | output "networks" {
21 | value = data.netmaker_networks.example.networks
22 | }
--------------------------------------------------------------------------------
/examples/provider/provider.tf:
--------------------------------------------------------------------------------
1 | provider "netmaker" {
2 | # example configuration here
3 | username = ""
4 | password = ""
5 | host = ""
6 | }
--------------------------------------------------------------------------------
/examples/resources/netmaker_network/resource.tf:
--------------------------------------------------------------------------------
1 | resource "netmaker_network" "example" {
2 | sample_attribute = "foo"
3 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/madacluster/netmaker-terraform-provider
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/gravitl/netmaker v0.9.0
7 | github.com/hashicorp/terraform-plugin-docs v0.5.1
8 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0
9 | github.com/kyoh86/richgo v0.3.10 // indirect
10 | golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
11 | golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/helper/access_key.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 | "strings"
8 |
9 | "github.com/gravitl/netmaker/models"
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11 | )
12 |
13 | func (c *Client) CreateAccessKeyFromSchema(d *schema.ResourceData, netID string) (*models.AccessKey, error) {
14 | key := CreateAccessKeyFromSchema(d)
15 | return c.CreateKey(netID, *key)
16 | }
17 |
18 | func (c *Client) CreateKey(networkID string, key models.AccessKey) (*models.AccessKey, error) {
19 | rb, err := json.Marshal(key)
20 | if err != nil {
21 | return nil, err
22 | }
23 |
24 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/networks/%s/keys", c.HostURL, networkID), strings.NewReader(string(rb)))
25 | if err != nil {
26 | return nil, err
27 | }
28 | body, err := c.doRequest(req)
29 | if err != nil {
30 | return nil, err
31 | }
32 |
33 | err = json.Unmarshal(body, &key)
34 | if err != nil {
35 | return nil, err
36 | }
37 |
38 | return &key, nil
39 | }
40 | func CreateAccessKeyFromSchema(d *schema.ResourceData) *models.AccessKey {
41 | return &models.AccessKey{
42 | Name: d.Get("name").(string),
43 | AccessString: d.Get("key").(string),
44 | Uses: d.Get("uses").(int),
45 | }
46 | }
47 | func (c *Client) GetKeys(networkID string) ([]models.AccessKey, error) {
48 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/networks/%s/keys", c.HostURL, networkID), nil)
49 | if err != nil {
50 | return nil, err
51 | }
52 | body, err := c.doRequest(req)
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | var key []models.AccessKey
58 | err = json.Unmarshal(body, &key)
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | return key, nil
64 | }
65 |
66 | func (c *Client) GetKey(networkID string, keyID string) (*models.AccessKey, error) {
67 | keys, err := c.GetKeys(networkID)
68 | if err != nil {
69 | return nil, err
70 | }
71 | for _, key := range keys {
72 | if key.Name == keyID {
73 | return &key, nil
74 | }
75 | }
76 | return nil, nil
77 | }
78 |
79 | func (c *Client) DeleteKey(networkID string, keyID string) error {
80 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/networks/%s/keys/%s", c.HostURL, networkID, keyID), nil)
81 | if err != nil {
82 | return err
83 | }
84 | _, err = c.doRequest(req)
85 | if err != nil {
86 | return err
87 | }
88 |
89 | return nil
90 | }
91 |
92 | func CreateAccessKeySchema() map[string]*schema.Schema {
93 | return map[string]*schema.Schema{
94 | "name": {
95 | Type: schema.TypeString,
96 | Required: true,
97 | Description: "Name of the access key",
98 | },
99 | "netid": {
100 | Type: schema.TypeString,
101 | Required: true,
102 | Description: "Name of the access key",
103 | ForceNew: true,
104 | },
105 | "key": {
106 | Type: schema.TypeString,
107 | Computed: true,
108 | Description: "Key of the access key",
109 | },
110 | "uses": {
111 | Type: schema.TypeInt,
112 | // Computed: true,
113 | Required: true,
114 | Description: "Uses of the access key",
115 | },
116 | }
117 | }
118 |
119 | func SetAccessKeySchemaData(d *schema.ResourceData, key *models.AccessKey, netID string) error {
120 | ID := fmt.Sprintf("%s-%s", netID, key.Name)
121 | d.SetId(ID)
122 | d.Set("name", key.Name)
123 | d.Set("key", key.AccessString)
124 | d.Set("uses", key.Uses)
125 | return nil
126 | }
127 |
128 | func (c *Client) UpdateKey(networkID string, key models.AccessKey) error {
129 | rb, err := json.Marshal(key)
130 | if err != nil {
131 | return err
132 | }
133 |
134 | req, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/networks/%s/keyupdate", c.HostURL, networkID), strings.NewReader(string(rb)))
135 | if err != nil {
136 | return err
137 | }
138 | _, err = c.doRequest(req)
139 | if err != nil {
140 | return err
141 | }
142 |
143 | return nil
144 | }
145 |
146 | func (c *Client) UpdateKeyFromSchema(d *schema.ResourceData, netID string) error {
147 | key := CreateAccessKeyFromSchema(d)
148 | return c.UpdateKey(netID, *key)
149 | }
150 |
--------------------------------------------------------------------------------
/helper/access_key_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "net/http"
5 | "reflect"
6 | "testing"
7 |
8 | "github.com/gravitl/netmaker/models"
9 | )
10 |
11 | func TestClient_CreateKey(t *testing.T) {
12 | type fields struct {
13 | HostURL string
14 | HTTPClient *http.Client
15 | Token string
16 | Auth AuthStruct
17 | }
18 | type args struct {
19 | networkID string
20 | key models.AccessKey
21 | }
22 | tests := []struct {
23 | name string
24 | fields fields
25 | args args
26 | want *models.AccessKey
27 | wantErr bool
28 | }{
29 | // TODO: Add test cases.
30 | {
31 | name: "Create admin user",
32 | fields: fields{
33 | HostURL: host,
34 | HTTPClient: &http.Client{},
35 | Auth: AuthStruct{
36 | Username: user,
37 | Password: pass,
38 | },
39 | },
40 | args: args{
41 | networkID: "netmakertest",
42 | key: models.AccessKey{
43 | Name: "test",
44 | Uses: 10,
45 | },
46 | },
47 | want: &models.AccessKey{
48 | Name: "test",
49 | Uses: 10,
50 | },
51 | },
52 | }
53 | for _, tt := range tests {
54 | t.Run(tt.name, func(t *testing.T) {
55 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
56 | got, err := c.CreateKey(tt.args.networkID, tt.args.key)
57 | if (err != nil) != tt.wantErr {
58 | t.Errorf("Client.CreateKey() error = %v, wantErr %v", err, tt.wantErr)
59 | return
60 | }
61 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
62 | t.Errorf("Client.CreateKey() = %v, want %v", got, tt.want)
63 | }
64 | })
65 | }
66 | }
67 |
68 | func TestClient_GetKey(t *testing.T) {
69 | type fields struct {
70 | HostURL string
71 | HTTPClient *http.Client
72 | Token string
73 | Auth AuthStruct
74 | }
75 | type args struct {
76 | networkID string
77 | accesKeyID string
78 | }
79 | tests := []struct {
80 | name string
81 | fields fields
82 | args args
83 | want *models.AccessKey
84 | wantErr bool
85 | }{
86 | // TODO: Add test cases.
87 | {
88 | name: "Get admin user",
89 | fields: fields{
90 | HostURL: host,
91 | HTTPClient: &http.Client{},
92 | Auth: AuthStruct{
93 | Username: user,
94 | Password: pass,
95 | },
96 | },
97 | args: args{
98 | networkID: "netmakertest",
99 | accesKeyID: "test",
100 | },
101 | want: &models.AccessKey{
102 | Name: "test",
103 | Uses: 10,
104 | },
105 | },
106 | }
107 | for _, tt := range tests {
108 | t.Run(tt.name, func(t *testing.T) {
109 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
110 | got, err := c.GetKey(tt.args.networkID, tt.args.accesKeyID)
111 | if (err != nil) != tt.wantErr {
112 | t.Errorf("Client.GetKey() error = %v, wantErr %v", err, tt.wantErr)
113 | return
114 | }
115 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
116 | t.Errorf("Client.GetKey() = %v, want %v", got, tt.want)
117 | }
118 | })
119 | }
120 | }
121 |
122 | func TestClient_GetKeys(t *testing.T) {
123 | type fields struct {
124 | HostURL string
125 | HTTPClient *http.Client
126 | Token string
127 | Auth AuthStruct
128 | }
129 | type args struct {
130 | networkID string
131 | }
132 | tests := []struct {
133 | name string
134 | fields fields
135 | args args
136 | want []models.AccessKey
137 | wantErr bool
138 | }{
139 | // TODO: Add test cases.
140 | }
141 | for _, tt := range tests {
142 | t.Run(tt.name, func(t *testing.T) {
143 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
144 |
145 | got, err := c.GetKeys(tt.args.networkID)
146 | if (err != nil) != tt.wantErr {
147 | t.Errorf("Client.GetKeys() error = %v, wantErr %v", err, tt.wantErr)
148 | return
149 | }
150 | if !reflect.DeepEqual(got[0].Name, tt.want[0].Name) {
151 | t.Errorf("Client.GetKeys() = %v, want %v", got, tt.want)
152 | }
153 | })
154 | }
155 | }
156 |
157 | func TestClient_DeleteKey(t *testing.T) {
158 | type fields struct {
159 | HostURL string
160 | HTTPClient *http.Client
161 | Token string
162 | Auth AuthStruct
163 | }
164 | type args struct {
165 | networkID string
166 | keyID string
167 | }
168 | tests := []struct {
169 | name string
170 | fields fields
171 | args args
172 | wantErr bool
173 | }{
174 | // TODO: Add test cases.
175 |
176 | {
177 | name: "Create admin user",
178 | fields: fields{
179 | HostURL: host,
180 | HTTPClient: &http.Client{},
181 | Auth: AuthStruct{
182 | Username: user,
183 | Password: pass,
184 | },
185 | },
186 | args: args{
187 | networkID: "netmakertest",
188 | keyID: "test",
189 | },
190 | },
191 | }
192 | for _, tt := range tests {
193 | t.Run(tt.name, func(t *testing.T) {
194 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
195 |
196 | if err := c.DeleteKey(tt.args.networkID, tt.args.keyID); (err != nil) != tt.wantErr {
197 | t.Errorf("Client.DeleteKey() error = %v, wantErr %v", err, tt.wantErr)
198 | }
199 | })
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/helper/auth.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 | "strings"
8 | )
9 |
10 | // SignIn -
11 | func (c *Client) SignIn() (*AuthResponse, error) {
12 | if c.Auth.Username == "" || c.Auth.Password == "" {
13 | return nil, fmt.Errorf("define username and password")
14 | }
15 | rb, err := json.Marshal(c.Auth)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/users/adm/authenticate", c.HostURL), strings.NewReader(string(rb)))
21 | if err != nil {
22 | return nil, err
23 | }
24 |
25 | body, err := c.doRequest(req)
26 | if err != nil {
27 | return nil, err
28 | }
29 |
30 | ar := AuthResponse{}
31 | err = json.Unmarshal(body, &ar)
32 | if err != nil {
33 | return nil, err
34 | }
35 |
36 | return &ar, nil
37 | }
38 |
--------------------------------------------------------------------------------
/helper/auth_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "net/http"
5 | "reflect"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestClient_SignIn(t *testing.T) {
11 | type fields struct {
12 | HostURL string
13 | HTTPClient *http.Client
14 | Token string
15 | Auth AuthStruct
16 | }
17 |
18 | tests := []struct {
19 | name string
20 | fields fields
21 | want *AuthResponse
22 | wantErr bool
23 | }{
24 | // TODO: Add test cases.
25 | {"",
26 | fields{
27 | "http://localhost:8081",
28 | &http.Client{Timeout: 10 * time.Second},
29 | "",
30 | AuthStruct{
31 | "admin",
32 | "mx4S6JsSg7JWcZ",
33 | },
34 | },
35 | &AuthResponse{
36 | Code: 200,
37 | },
38 | false,
39 | },
40 | }
41 | for _, tt := range tests {
42 | t.Run(tt.name, func(t *testing.T) {
43 | c := &Client{
44 | HostURL: tt.fields.HostURL,
45 | HTTPClient: tt.fields.HTTPClient,
46 | Token: tt.fields.Token,
47 | Auth: tt.fields.Auth,
48 | }
49 | got, err := c.SignIn()
50 | if (err != nil) != tt.wantErr {
51 | t.Errorf("Client.SignIn() error = %v, wantErr %v", err, tt.wantErr)
52 | return
53 | }
54 | if !reflect.DeepEqual(got.Code, tt.want.Code) {
55 | t.Errorf("Client.SignIn() = %v, want %v", got, tt.want)
56 | }
57 | })
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/helper/client.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "net/http"
7 | "time"
8 |
9 | "github.com/gravitl/netmaker/models"
10 | )
11 |
12 | // AuthStruct -
13 | type AuthStruct struct {
14 | Username string `json:"username"`
15 | Password string `json:"password"`
16 | }
17 |
18 | // AuthResponse -
19 | type AuthResponse struct {
20 | Code int `json:"Code"`
21 | Message string `json:"Message"`
22 | Response struct {
23 | UserName string `json:"UserName"`
24 | AuthToken string `json:"AuthToken"`
25 | } `json:"Response"`
26 | }
27 |
28 | // Client -
29 | type Client struct {
30 | HostURL string
31 | HTTPClient *http.Client
32 | Token string
33 | Auth AuthStruct
34 | }
35 |
36 | const HostURL string = "http://localhost:19090"
37 |
38 | func NewClient(host, username, password *string) (*Client, error) {
39 |
40 | c := Client{
41 | HTTPClient: &http.Client{Timeout: 10 * time.Second},
42 | // Default Hashicups URL
43 | HostURL: HostURL,
44 | Auth: AuthStruct{
45 | Username: *username,
46 | Password: *password,
47 | },
48 | }
49 |
50 | if host != nil {
51 | c.HostURL = *host
52 | }
53 | admin, err := c.CheckAdmin()
54 | if err != nil {
55 | return nil, err
56 | }
57 | if !admin {
58 | user := models.User{
59 | UserName: *username,
60 | Password: *password,
61 | }
62 | c.CreateAdmin(user)
63 | }
64 | ar, err := c.SignIn()
65 | if err != nil {
66 | return nil, err
67 | }
68 |
69 | c.Token = ar.Response.AuthToken
70 |
71 | return &c, nil
72 |
73 | }
74 |
75 | func (c *Client) doRequest(req *http.Request) ([]byte, error) {
76 | req.Header.Set("Authorization", "Bearer "+c.Token)
77 |
78 | res, err := c.HTTPClient.Do(req)
79 | if err != nil {
80 | return nil, err
81 | }
82 | defer res.Body.Close()
83 |
84 | body, err := ioutil.ReadAll(res.Body)
85 | if err != nil {
86 | return nil, err
87 | }
88 |
89 | if res.StatusCode != http.StatusOK {
90 | return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body)
91 | }
92 |
93 | return body, err
94 | }
95 |
--------------------------------------------------------------------------------
/helper/network.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 | "strconv"
8 | "strings"
9 |
10 | "github.com/gravitl/netmaker/models"
11 |
12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13 | )
14 |
15 | // GetNetworks - Returns list of coffees (no auth required)
16 | func (c *Client) GetNetworks() ([]models.Network, error) {
17 |
18 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/networks", c.HostURL), nil)
19 | if err != nil {
20 | return nil, err
21 | }
22 |
23 | body, err := c.doRequest(req)
24 | if err != nil {
25 | return nil, err
26 | }
27 | networks := []models.Network{}
28 | err = json.Unmarshal(body, &networks)
29 | if err != nil {
30 | return nil, err
31 | }
32 |
33 | return networks, nil
34 | }
35 |
36 | // GetNetworks - Returns a network by ID
37 | func (c *Client) GetNetwork(networkID string) (*models.Network, error) {
38 |
39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/networks/%s", c.HostURL, networkID), nil)
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | body, err := c.doRequest(req)
45 | if err != nil {
46 | return nil, err
47 | }
48 | network := models.Network{}
49 | err = json.Unmarshal(body, &network)
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | return &network, nil
55 | }
56 |
57 | func (c *Client) CreateNetworkFromSchema(d *schema.ResourceData) (*models.Network, error) {
58 | network := CreateNetworkFromSchemaData(d)
59 | return c.CreateNetwork(*network)
60 | }
61 |
62 | // GetNetworks - Create a new network
63 | func (c *Client) CreateNetwork(network models.Network) (*models.Network, error) {
64 |
65 | rb, err := json.Marshal(network)
66 | if err != nil {
67 | return nil, err
68 | }
69 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/networks", c.HostURL), strings.NewReader(string(rb)))
70 | if err != nil {
71 | return nil, err
72 | }
73 |
74 | _, err = c.doRequest(req)
75 | if err != nil {
76 | return nil, err
77 | }
78 |
79 | return &network, nil
80 | }
81 |
82 | // UpdateNetwork - Updates a network
83 | func (c *Client) UpdateNetwork(network models.Network) (*models.Network, error) {
84 |
85 | rb, err := json.Marshal(network)
86 | if err != nil {
87 | return nil, err
88 | }
89 | req, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/networks/%s", c.HostURL, network.NetID), strings.NewReader(string(rb)))
90 | if err != nil {
91 | return nil, err
92 | }
93 |
94 | body, err := c.doRequest(req)
95 | if err != nil {
96 | return nil, err
97 | }
98 | network = models.Network{}
99 | err = json.Unmarshal(body, &network)
100 | if err != nil {
101 | return nil, err
102 | }
103 |
104 | return &network, nil
105 | }
106 |
107 | func (c *Client) UpdateNetworkFromSchema(d *schema.ResourceData) (*models.Network, error) {
108 | network := CreateNetworkFromSchemaData(d)
109 | return c.UpdateNetwork(*network)
110 | }
111 |
112 | func (c *Client) UpdateNetworkMap(data map[string]string) (*models.Network, error) {
113 | network := models.Network{}
114 | mapFiels(data, &network)
115 | return c.UpdateNetwork(network)
116 | }
117 |
118 | func (c *Client) DeleteNetwork(networkID string) error {
119 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/networks/%s", c.HostURL, networkID), nil)
120 | if err != nil {
121 | return err
122 | }
123 |
124 | _, err = c.doRequest(req)
125 | if err != nil {
126 | return err
127 | }
128 |
129 | return nil
130 | }
131 |
132 | func mapFiels(data map[string]string, network *models.Network) {
133 | for k, v := range data {
134 | switch k {
135 | case "displayname":
136 | network.DisplayName = v
137 | case "addressrange":
138 | network.AddressRange = v
139 | case "netid":
140 | network.NetID = v
141 | case "islocal":
142 | network.IsLocal = v
143 | case "isDualStack":
144 | network.IsDualStack = v
145 | case "isIPv4":
146 | network.IsIPv4 = v
147 | case "isIPv6":
148 | network.IsIPv6 = v
149 | case "isGRPCHub":
150 | network.IsGRPCHub = v
151 | case "localrange":
152 | network.LocalRange = v
153 | case "checkininterval":
154 | i, _ := strconv.Atoi(v)
155 | network.DefaultCheckInInterval = int32(i)
156 | case "defaultudpholepunch":
157 | network.DefaultUDPHolePunch = v
158 | case "defaultextclientdns":
159 | network.DefaultExtClientDNS = v
160 | case "defaultmtu":
161 | i, _ := strconv.Atoi(v)
162 | network.DefaultMTU = int32(i)
163 | case "defaultkeepalive":
164 | i, _ := strconv.Atoi(v)
165 | network.DefaultKeepalive = int32(i)
166 | case "allowmanualsignup":
167 | network.AllowManualSignUp = v
168 | case "nodeslastmodified":
169 | network.NodesLastModified, _ = strconv.ParseInt(v, 10, 64)
170 | case "defaultinterface":
171 | network.DefaultInterface = v
172 | case "defaultlistenport":
173 | i, _ := strconv.ParseInt(v, 10, 32)
174 | network.DefaultListenPort = int32(i)
175 | case "defaultsaveconfig":
176 | network.DefaultSaveConfig = v
177 | case "nodelimit":
178 | i, _ := strconv.ParseInt(v, 10, 32)
179 | network.NodeLimit = int32(i)
180 | case "defaultpostup":
181 | network.DefaultPostUp = v
182 | case "defaultpostdown":
183 | network.DefaultPostDown = v
184 | }
185 | }
186 | }
187 |
188 | func mapFielsRevert(network *models.Network) map[string]string {
189 | data := make(map[string]string)
190 | data["displayname"] = network.DisplayName
191 | data["addressrange"] = network.AddressRange
192 | data["netid"] = network.NetID
193 | data["defaultinterface"] = network.DefaultInterface
194 | data["defaultlistenport"] = strconv.FormatInt(int64(network.DefaultListenPort), 10)
195 | data["nodelimit"] = strconv.FormatInt(int64(network.NodeLimit), 10)
196 | data["defaultpostup"] = network.DefaultPostUp
197 | data["defaultpostdown"] = network.DefaultPostDown
198 | data["defaultsaveconfig"] = network.DefaultSaveConfig
199 | data["defaultmtu"] = strconv.FormatInt(int64(network.DefaultMTU), 10)
200 | data["defaultkeepalive"] = strconv.FormatInt(int64(network.DefaultKeepalive), 10)
201 | data["allowmanualsignup"] = network.AllowManualSignUp
202 | data["defaultudpholepunch"] = network.DefaultUDPHolePunch
203 | data["defaultextclientdns"] = network.DefaultExtClientDNS
204 | data["islocal"] = network.IsLocal
205 | data["isDualStack"] = network.IsDualStack
206 | data["isIPv4"] = network.IsIPv4
207 | data["isIPv6"] = network.IsIPv6
208 | data["isGRPCHub"] = network.IsGRPCHub
209 | data["localrange"] = network.LocalRange
210 | data["checkininterval"] = strconv.FormatInt(int64(network.DefaultCheckInInterval), 10)
211 | return data
212 | }
213 |
214 | func CreateNetworkSchema() map[string]*schema.Schema {
215 | return map[string]*schema.Schema{
216 | "netid": {
217 | Type: schema.TypeString,
218 | Required: true,
219 | },
220 | "addressrange": {
221 | Type: schema.TypeString,
222 | Required: true,
223 | },
224 | "addressrange6": {
225 | Type: schema.TypeString,
226 | Computed: true,
227 | },
228 | "displayname": {
229 | Type: schema.TypeString,
230 | Computed: true,
231 | },
232 | "islocal": {
233 | Type: schema.TypeString,
234 | Computed: true,
235 | Default: nil,
236 | },
237 | "isdualstack": {
238 | Type: schema.TypeString,
239 | Computed: true,
240 | Default: nil,
241 | },
242 | "isipv4": {
243 | Type: schema.TypeString,
244 | Computed: true,
245 | },
246 | "isipv6": {
247 | Type: schema.TypeString,
248 | Computed: true,
249 | },
250 | "isgrpchub": {
251 | Type: schema.TypeString,
252 | Computed: true,
253 | },
254 | "localrange": {
255 | Type: schema.TypeString,
256 | Computed: true,
257 | },
258 | "checkininterval": {
259 | Type: schema.TypeInt,
260 | Computed: true,
261 | },
262 |
263 | "defaultudpholepunch": {
264 | Type: schema.TypeString,
265 | Computed: true,
266 | Default: nil,
267 | },
268 | "defaultextclientdns": {
269 | Type: schema.TypeString,
270 | Computed: true,
271 | },
272 | "defaultmtu": {
273 | Type: schema.TypeInt,
274 | Computed: true,
275 | },
276 | "defaultkeepalive": {
277 | Type: schema.TypeInt,
278 | Computed: true,
279 | },
280 | "allowmanualsignup": {
281 | Type: schema.TypeString,
282 | Computed: true,
283 | },
284 | "defaultinterface": {
285 | Type: schema.TypeString,
286 | Computed: true,
287 | },
288 | "defaultlistenport": {
289 | Type: schema.TypeInt,
290 | Computed: true,
291 | },
292 | "defaultsaveconfig": {
293 | Type: schema.TypeString,
294 | Computed: true,
295 | },
296 | "nodelimit": {
297 | Type: schema.TypeInt,
298 | Computed: true,
299 | },
300 | "defaultpostup": {
301 | Type: schema.TypeString,
302 | Computed: true,
303 | },
304 | "defaultpostdown": {
305 | Type: schema.TypeString,
306 | Computed: true,
307 | },
308 | }
309 |
310 | }
311 |
312 | func CreateNetworkFromSchemaData(d *schema.ResourceData) *models.Network {
313 | network := &models.Network{}
314 | network.NetID = d.Get("netid").(string)
315 | network.AddressRange = d.Get("addressrange").(string)
316 | network.AddressRange6 = d.Get("addressrange6").(string)
317 | network.DisplayName = d.Get("displayname").(string)
318 | network.IsLocal = d.Get("islocal").(string)
319 | network.IsDualStack = d.Get("isdualstack").(string)
320 | network.IsIPv4 = d.Get("isipv4").(string)
321 | network.IsIPv6 = d.Get("isipv6").(string)
322 | network.IsGRPCHub = d.Get("isgrpchub").(string)
323 | network.LocalRange = d.Get("localrange").(string)
324 | network.DefaultCheckInInterval = int32(d.Get("checkininterval").(int))
325 | network.DefaultUDPHolePunch = d.Get("defaultudpholepunch").(string)
326 | network.DefaultExtClientDNS = d.Get("defaultextclientdns").(string)
327 | network.DefaultMTU = int32(d.Get("defaultmtu").(int))
328 | network.AllowManualSignUp = d.Get("allowmanualsignup").(string)
329 | network.DefaultInterface = d.Get("defaultinterface").(string)
330 | network.DefaultListenPort = int32(d.Get("defaultlistenport").(int))
331 | network.DefaultSaveConfig = d.Get("defaultsaveconfig").(string)
332 | network.NodeLimit = int32(d.Get("nodelimit").(int))
333 | network.DefaultPostUp = d.Get("defaultpostup").(string)
334 | network.DefaultPostDown = d.Get("defaultpostdown").(string)
335 | return network
336 | }
337 |
338 | func SetNetworkSchemaData(d *schema.ResourceData, network *models.Network) error {
339 | if err := d.Set("netid", network.NetID); err != nil {
340 | return err
341 | }
342 | if err := d.Set("addressrange", network.AddressRange); err != nil {
343 | return err
344 | }
345 | if err := d.Set("addressrange6", network.AddressRange6); err != nil {
346 | return err
347 | }
348 | if err := d.Set("displayname", network.DisplayName); err != nil {
349 | return err
350 | }
351 | if err := d.Set("islocal", network.IsLocal); err != nil {
352 | return err
353 | }
354 | if err := d.Set("isdualstack", network.IsDualStack); err != nil {
355 | return err
356 | }
357 | if err := d.Set("isipv4", network.IsIPv4); err != nil {
358 | return err
359 | }
360 | if err := d.Set("isipv6", network.IsIPv6); err != nil {
361 | return err
362 | }
363 | if err := d.Set("isgrpchub", network.IsGRPCHub); err != nil {
364 | return err
365 | }
366 | if err := d.Set("localrange", network.LocalRange); err != nil {
367 | return err
368 | }
369 | if err := d.Set("checkininterval", network.DefaultCheckInInterval); err != nil {
370 | return err
371 | }
372 |
373 | if err := d.Set("defaultudpholepunch", network.DefaultUDPHolePunch); err != nil {
374 | return err
375 | }
376 | if err := d.Set("defaultextclientdns", network.DefaultExtClientDNS); err != nil {
377 | return err
378 | }
379 | if err := d.Set("defaultmtu", network.DefaultMTU); err != nil {
380 | return err
381 | }
382 | if err := d.Set("defaultkeepalive", network.DefaultKeepalive); err != nil {
383 | return err
384 | }
385 | if err := d.Set("allowmanualsignup", network.AllowManualSignUp); err != nil {
386 | return err
387 | }
388 | return nil
389 | }
390 |
391 | func FlattenNetworkData(network *models.Network) map[string]interface{} {
392 | oi := make(map[string]interface{})
393 | oi["netid"] = network.NetID
394 | oi["addressrange"] = network.AddressRange
395 | oi["addressrange6"] = network.AddressRange6
396 | oi["displayname"] = network.DisplayName
397 | oi["islocal"] = network.IsLocal
398 | oi["isdualstack"] = network.IsDualStack
399 | oi["isipv4"] = network.IsIPv4
400 | oi["isipv6"] = network.IsIPv6
401 | oi["isgrpchub"] = network.IsGRPCHub
402 | oi["localrange"] = network.LocalRange
403 | oi["checkininterval"] = network.DefaultCheckInInterval
404 | oi["defaultinterface"] = network.DefaultInterface
405 | oi["defaultlistenport"] = network.DefaultListenPort
406 | oi["nodelimit"] = network.NodeLimit
407 | oi["defaultpostup"] = network.DefaultPostUp
408 | oi["defaultpostdown"] = network.DefaultPostDown
409 | oi["defaultsaveconfig"] = network.DefaultSaveConfig
410 | oi["defaultmtu"] = network.DefaultMTU
411 | oi["defaultkeepalive"] = network.DefaultKeepalive
412 | oi["allowmanualsignup"] = network.AllowManualSignUp
413 | oi["defaultudpholepunch"] = network.DefaultUDPHolePunch
414 | oi["defaultextclientdns"] = network.DefaultExtClientDNS
415 | oi["islocal"] = network.IsLocal
416 | oi["isdualstack"] = network.IsDualStack
417 | oi["isipv4"] = network.IsIPv4
418 | oi["isipv6"] = network.IsIPv6
419 | oi["isgrpchub"] = network.IsGRPCHub
420 | oi["localrange"] = network.LocalRange
421 | oi["checkininterval"] = network.DefaultCheckInInterval
422 |
423 | return oi
424 | }
425 |
426 | func FlattenNetworksData(networks *[]models.Network) []interface{} {
427 | if networks != nil {
428 | ois := make([]interface{}, len(*networks))
429 | for i, network := range *networks {
430 | ois[i] = FlattenNetworkData(&network)
431 | }
432 |
433 | return ois
434 | }
435 |
436 | return make([]interface{}, 0)
437 | }
438 |
--------------------------------------------------------------------------------
/helper/network_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "net/http"
5 | "reflect"
6 | "testing"
7 | "time"
8 |
9 | "github.com/gravitl/netmaker/models"
10 | )
11 |
12 | var host = "http://localhost:8081"
13 | var pass = "mx4S6JsSg7JWcZ"
14 | var user = "admin"
15 |
16 | const ipRange = "10.101.0.0/24"
17 |
18 | func TestClient_CreateNetwork(t *testing.T) {
19 | type fields struct {
20 | HostURL string
21 | HTTPClient *http.Client
22 | Token string
23 | Auth AuthStruct
24 | }
25 | type args struct {
26 | networkID string
27 | addressrange string
28 | localrange string
29 | islocal string
30 | isdualstack string
31 | addressrange6 string
32 | defaultudpholepunch string
33 | }
34 | tests := []struct {
35 | name string
36 | fields fields
37 | args args
38 | want *models.Network
39 | wantErr bool
40 | }{
41 | // TODO: Add test cases.
42 | {"",
43 | fields{
44 | "http://localhost:8081",
45 | &http.Client{Timeout: 10 * time.Second},
46 | "",
47 | AuthStruct{
48 | user,
49 | pass,
50 | },
51 | },
52 | args{
53 | networkID: "netmakertest",
54 | addressrange: "10.101.10.0/24",
55 | localrange: "",
56 | islocal: "no",
57 | isdualstack: "no",
58 | addressrange6: "",
59 | defaultudpholepunch: "yes",
60 | },
61 | &models.Network{
62 | AddressRange: "10.101.10.0/24",
63 | NetID: "netmakertest",
64 | },
65 |
66 | false,
67 | },
68 | }
69 | for _, tt := range tests {
70 | t.Run(tt.name, func(t *testing.T) {
71 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
72 | network := &models.Network{
73 | AddressRange: tt.args.addressrange,
74 | LocalRange: tt.args.localrange,
75 | IsLocal: tt.args.islocal,
76 | IsDualStack: tt.args.isdualstack,
77 | AddressRange6: tt.args.addressrange6,
78 | DefaultUDPHolePunch: tt.args.defaultudpholepunch,
79 | NetID: tt.args.networkID,
80 | }
81 | got, err := c.CreateNetwork(*network)
82 | if (err != nil) != tt.wantErr {
83 | t.Errorf("Client.CreateNetwork() error = %v, wantErr %v", err, tt.wantErr)
84 | return
85 | }
86 | if !reflect.DeepEqual(got.NetID, tt.want.NetID) {
87 | t.Errorf("Client.CreateNetwork() = %v, want %v", got, tt.want)
88 | }
89 | })
90 | }
91 | }
92 |
93 | func TestClient_GetNetworks(t *testing.T) {
94 | type fields struct {
95 | HostURL string
96 | HTTPClient *http.Client
97 | Token string
98 | Auth AuthStruct
99 | }
100 | tests := []struct {
101 | name string
102 | fields fields
103 | want []models.Network
104 | wantErr bool
105 | }{
106 | // TODO: Add test cases.
107 | {"",
108 | fields{
109 | "http://localhost:8081",
110 | &http.Client{Timeout: 10 * time.Second},
111 | "",
112 | AuthStruct{
113 | "admin",
114 | "mx4S6JsSg7JWcZ",
115 | },
116 | },
117 | []models.Network{
118 | {
119 | AddressRange: ipRange,
120 | NetID: "netmakertest",
121 | },
122 | },
123 | false,
124 | },
125 | }
126 | for _, tt := range tests {
127 | t.Run(tt.name, func(t *testing.T) {
128 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
129 | got, err := c.GetNetworks()
130 | if (err != nil) != tt.wantErr {
131 | t.Errorf("Client.GetNetworks() error = %v, wantErr %v", err, tt.wantErr)
132 | return
133 | }
134 | if !reflect.DeepEqual(got[0].NetID, tt.want[0].NetID) {
135 | t.Errorf("Client.GetNetworks() = %v, want %v", got, tt.want)
136 | }
137 | })
138 | }
139 | }
140 |
141 | func TestClient_GetNetwork(t *testing.T) {
142 | type fields struct {
143 | HostURL string
144 | HTTPClient *http.Client
145 | Token string
146 | Auth AuthStruct
147 | }
148 | type args struct {
149 | networkID string
150 | }
151 | tests := []struct {
152 | name string
153 | fields fields
154 | args args
155 | want *models.Network
156 | wantErr bool
157 | }{
158 | // TODO: Add test cases.
159 | {"",
160 | fields{
161 | "http://localhost:8081",
162 | &http.Client{Timeout: 10 * time.Second},
163 | "",
164 | AuthStruct{
165 | "admin",
166 | "mx4S6JsSg7JWcZ",
167 | },
168 | },
169 | args{networkID: "netmakertest"},
170 | &models.Network{
171 | AddressRange: "10.101.10.0/24",
172 | NetID: "netmakertest",
173 | DefaultUDPHolePunch: "yes",
174 | IsDualStack: "no",
175 | IsLocal: "no",
176 | },
177 |
178 | false,
179 | },
180 | }
181 | for _, tt := range tests {
182 | t.Run(tt.name, func(t *testing.T) {
183 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
184 |
185 | got, err := c.GetNetwork(tt.args.networkID)
186 | if (err != nil) != tt.wantErr {
187 | t.Errorf("Client.GetNetwork() error = %v, wantErr %v", err, tt.wantErr)
188 | return
189 | }
190 | if !reflect.DeepEqual(got.AddressRange, tt.want.AddressRange) {
191 | t.Errorf("Client.GetNetwork() = %v, want %v", got, tt.want)
192 | }
193 | })
194 | }
195 | }
196 |
197 | func TestClient_UpdateNetwork(t *testing.T) {
198 | type fields struct {
199 | HostURL string
200 | HTTPClient *http.Client
201 | Token string
202 | Auth AuthStruct
203 | }
204 | type args struct {
205 | data map[string]string
206 | }
207 | c, err := NewClient(&host, &user, &pass)
208 | if err != nil {
209 | t.Errorf("Client.UpdateNetwork() error = %v", err)
210 | }
211 |
212 | tests := []struct {
213 | name string
214 | fields fields
215 | args args
216 | want *models.Network
217 | wantErr bool
218 | }{
219 | // TODO: Add test cases.
220 | {"",
221 | fields{
222 | "http://localhost:8081",
223 | &http.Client{Timeout: 10 * time.Second},
224 | "",
225 | AuthStruct{
226 | "admin",
227 | "mx4S6JsSg7JWcZ",
228 | },
229 | },
230 | args{
231 | data: map[string]string{
232 | "netid": "netmakertest",
233 | "addressrange": "10.102.0.0/24",
234 | },
235 | },
236 | &models.Network{
237 | AddressRange: "10.102.0.0/24",
238 | NetID: "netmakertest",
239 | DisplayName: "netmakertest",
240 | },
241 |
242 | false,
243 | },
244 | }
245 | for _, tt := range tests {
246 | t.Run(tt.name, func(t *testing.T) {
247 | network, err := c.GetNetwork(tt.args.data["netid"])
248 | if err != nil {
249 | t.Errorf("Client.UpdateNetwork() error = %v", err)
250 | }
251 |
252 | networkFieldMap := mapFielsRevert(network)
253 | got, err := c.UpdateNetworkMap(networkFieldMap)
254 | if (err != nil) != tt.wantErr {
255 | t.Errorf("Client.UpdateNetwork() error = %v, wantErr %v", err, tt.wantErr)
256 | return
257 | }
258 | if !reflect.DeepEqual(got.DisplayName, tt.want.DisplayName) {
259 | t.Errorf("Client.UpdateNetwork() = %v, want %v", got, tt.want)
260 | }
261 | })
262 | }
263 | }
264 |
265 | func TestClient_DeleteNetwork(t *testing.T) {
266 | type fields struct {
267 | HostURL string
268 | HTTPClient *http.Client
269 | Token string
270 | Auth AuthStruct
271 | }
272 | type args struct {
273 | networkID string
274 | }
275 | tests := []struct {
276 | name string
277 | fields fields
278 | args args
279 | wantErr bool
280 | }{
281 | // TODO: Add test cases.
282 | {"",
283 | fields{
284 | "http://localhost:8081",
285 | &http.Client{Timeout: 10 * time.Second},
286 | "",
287 | AuthStruct{
288 | "admin",
289 | "mx4S6JsSg7JWcZ",
290 | },
291 | },
292 | args{networkID: "netmakertest"},
293 | false,
294 | },
295 | }
296 | for _, tt := range tests {
297 | t.Run(tt.name, func(t *testing.T) {
298 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
299 |
300 | if err := c.DeleteNetwork(tt.args.networkID); (err != nil) != tt.wantErr {
301 | t.Errorf("Client.DeleteNetwork() error = %v, wantErr %v", err, tt.wantErr)
302 | }
303 | })
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/helper/node.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 | "strings"
8 |
9 | "github.com/gravitl/netmaker/models"
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11 | )
12 |
13 | func (c *Client) GetNodes() ([]models.Node, error) {
14 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/nodes", c.HostURL), nil)
15 | if err != nil {
16 | return nil, err
17 | }
18 | body, err := c.doRequest(req)
19 | if err != nil {
20 | return nil, err
21 | }
22 | nodes := []models.Node{}
23 | err = json.Unmarshal(body, &nodes)
24 | if err != nil {
25 | return nil, err
26 | }
27 | return nodes, nil
28 | }
29 |
30 | func (c *Client) GetNetworkNodes(networkID string) ([]models.Node, error) {
31 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/nodes/%s", c.HostURL, networkID), nil)
32 | if err != nil {
33 | return nil, err
34 | }
35 | body, err := c.doRequest(req)
36 | if err != nil {
37 | return nil, err
38 | }
39 | nodes := []models.Node{}
40 | err = json.Unmarshal(body, &nodes)
41 | if err != nil {
42 | return nil, err
43 | }
44 | return nodes, nil
45 | }
46 |
47 | func (c *Client) CreateNetworkNode(networkID string, node models.Node) (*models.Node, error) {
48 | rb, err := json.Marshal(node)
49 | if err != nil {
50 | return nil, err
51 | }
52 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/nodes/%s", c.HostURL, networkID), strings.NewReader(string(rb)))
53 | if err != nil {
54 | return nil, err
55 | }
56 | body, err := c.doRequest(req)
57 | if err != nil {
58 | return nil, err
59 | }
60 | node = models.Node{}
61 | err = json.Unmarshal(body, &node)
62 | if err != nil {
63 | return nil, err
64 | }
65 | return &node, nil
66 | }
67 |
68 | func (c *Client) DeleteNetworkNode(networkID, mac string) error {
69 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/nodes/%s/%s", c.HostURL, networkID, mac), nil)
70 | if err != nil {
71 | return err
72 | }
73 | _, err = c.doRequest(req)
74 | if err != nil {
75 | return err
76 | }
77 | return nil
78 | }
79 |
80 | func (c *Client) GetNode(networkID, mac string) (*models.Node, error) {
81 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/nodes/%s/%s", c.HostURL, networkID, mac), nil)
82 | if err != nil {
83 | return nil, err
84 | }
85 | body, err := c.doRequest(req)
86 | if err != nil {
87 | return nil, err
88 | }
89 | node := &models.Node{}
90 | err = json.Unmarshal(body, node)
91 | if err != nil {
92 | return nil, err
93 | }
94 | return node, nil
95 | }
96 |
97 | func (c *Client) GetNetworkIngress(networkID string) ([]models.Node, error) {
98 | nodes, err := c.GetNetworkNodes(networkID)
99 | if err != nil {
100 | return nil, err
101 | }
102 | filter := []models.Node{}
103 | for _, node := range nodes {
104 | if node.IsIngressGateway == "yes" {
105 | filter = append(filter, node)
106 | }
107 | }
108 | return filter, nil
109 | }
110 |
111 | func (c *Client) CreateIngress(networkID, mac string) (*models.Node, error) {
112 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/nodes/%s/%s/createingress", c.HostURL, networkID, mac), nil)
113 | if err != nil {
114 | return nil, err
115 | }
116 | body, err := c.doRequest(req)
117 | if err != nil {
118 | return nil, err
119 | }
120 | node := &models.Node{}
121 |
122 | err = json.Unmarshal(body, node)
123 | if err != nil {
124 | return nil, err
125 | }
126 | return node, nil
127 | }
128 |
129 | func (c *Client) DeleteIngress(networkID, mac string) (*models.Node, error) {
130 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/nodes/%s/%s/deleteingress", c.HostURL, networkID, mac), nil)
131 | if err != nil {
132 | return nil, err
133 | }
134 | body, err := c.doRequest(req)
135 | if err != nil {
136 | return nil, err
137 | }
138 | node := &models.Node{}
139 |
140 | err = json.Unmarshal(body, node)
141 | if err != nil {
142 | return nil, err
143 | }
144 | return node, nil
145 | }
146 |
147 | func (c *Client) GetNetworkEgress(networkID string) ([]models.Node, error) {
148 | nodes, err := c.GetNetworkNodes(networkID)
149 | if err != nil {
150 | return nil, err
151 | }
152 | filter := []models.Node{}
153 | for _, node := range nodes {
154 | if node.IsEgressGateway == "yes" {
155 | filter = append(filter, node)
156 | }
157 | }
158 | return filter, nil
159 | }
160 |
161 | func (c *Client) CreateEgress(networkID, mac string, gateway *models.EgressGatewayRequest) (*models.Node, error) {
162 | rb, err := json.Marshal(gateway)
163 | if err != nil {
164 | return nil, err
165 | }
166 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/nodes/%s/%s/creategateway", c.HostURL, networkID, mac), strings.NewReader(string(rb)))
167 | if err != nil {
168 | return nil, err
169 | }
170 | body, err := c.doRequest(req)
171 | if err != nil {
172 | return nil, err
173 | }
174 | node := &models.Node{}
175 |
176 | err = json.Unmarshal(body, node)
177 | if err != nil {
178 | return nil, err
179 | }
180 | return node, nil
181 | }
182 |
183 | func (c *Client) DeleteEgress(networkID, mac string) (*models.Node, error) {
184 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/nodes/%s/%s/deletegateway", c.HostURL, networkID, mac), nil)
185 | if err != nil {
186 | return nil, err
187 | }
188 | body, err := c.doRequest(req)
189 | if err != nil {
190 | return nil, err
191 | }
192 | node := &models.Node{}
193 |
194 | err = json.Unmarshal(body, node)
195 | if err != nil {
196 | return nil, err
197 | }
198 | return node, nil
199 | }
200 |
201 | func CreateNodeDataSchema() map[string]*schema.Schema {
202 | return map[string]*schema.Schema{
203 | "name": {
204 | Type: schema.TypeString,
205 | Optional: true,
206 | Description: "The name of the node",
207 | },
208 | "mac": {
209 | Type: schema.TypeString,
210 | Required: true,
211 | Description: "The MAC address of the node",
212 | },
213 | "network_id": {
214 | Type: schema.TypeString,
215 | Required: true,
216 | Description: "The ID of the network the node belongs to",
217 | },
218 | "is_ingress_gateway": {
219 | Type: schema.TypeString,
220 | Optional: true,
221 | Description: "Is the node an ingress gateway",
222 | },
223 | "is_egress_gateway": {
224 | Type: schema.TypeString,
225 | Optional: true,
226 | Description: "Is the node an egress gateway",
227 | },
228 | }
229 | }
230 |
231 | func SetNodeSchemaData(d *schema.ResourceData, node *models.Node, networkID string) {
232 | d.SetId(node.ID)
233 | d.Set("name", node.Name)
234 | d.Set("mac", node.MacAddress)
235 | d.Set("network_id", node.Network)
236 | d.Set("is_ingress_gateway", node.IsIngressGateway)
237 | d.Set("is_egress_gateway", node.IsEgressGateway)
238 | }
239 |
240 | func CreateEgressSchema() map[string]*schema.Schema {
241 | return map[string]*schema.Schema{
242 | "mac": {
243 | Type: schema.TypeString,
244 | Required: true,
245 | Description: "The MAC address of the node",
246 | },
247 | "netid": {
248 | Type: schema.TypeString,
249 | Required: true,
250 | Description: "The ID of the network the node belongs to",
251 | },
252 | "interface": {
253 | Type: schema.TypeString,
254 | Optional: true,
255 | ForceNew: true,
256 | Description: "The interface the node is connected to",
257 | },
258 | "ranges": {
259 | Type: schema.TypeSet,
260 | Optional: true,
261 | Description: "The ranges the node is allowed to access",
262 | ForceNew: true,
263 | Elem: &schema.Schema{Type: schema.TypeString},
264 | },
265 | }
266 | }
267 |
268 | func CreateIngressSchema() map[string]*schema.Schema {
269 | return map[string]*schema.Schema{
270 | "mac": {
271 | Type: schema.TypeString,
272 | Required: true,
273 | Description: "The MAC address of the node",
274 | },
275 | "netid": {
276 | Type: schema.TypeString,
277 | Required: true,
278 | Description: "The ID of the network the node belongs to",
279 | },
280 | }
281 | }
282 |
283 | func CreateEgressFromSchema(d *schema.ResourceData) *models.EgressGatewayRequest {
284 |
285 | tfRanges := d.Get("ranges").(*schema.Set).List()
286 | ranges := make([]string, len(tfRanges))
287 | for i, tfTag := range tfRanges {
288 | ranges[i] = tfTag.(string)
289 | }
290 | return &models.EgressGatewayRequest{
291 | Interface: d.Get("interface").(string),
292 | Ranges: ranges,
293 | }
294 | }
295 |
296 | func (c *Client) CreateEgressFromSchema(d *schema.ResourceData, netID, mac string) (*models.Node, error) {
297 | egress := CreateEgressFromSchema(d)
298 | return c.CreateEgress(netID, mac, egress)
299 | }
300 |
301 | func SetEgressSchemaData(d *schema.ResourceData, node *models.Node, networkID, mac string) {
302 | d.SetId(node.ID)
303 | d.Set("mac", mac)
304 | d.Set("netid", node.Network)
305 | d.Set("interface", node.Interface)
306 | }
307 |
308 | func SetIngressSchemaData(d *schema.ResourceData, node *models.Node, networkID, mac string) {
309 | d.SetId(node.ID)
310 | d.Set("mac", mac)
311 | d.Set("netid", node.Network)
312 | }
313 |
--------------------------------------------------------------------------------
/helper/node_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "net/http"
5 | "reflect"
6 | "testing"
7 |
8 | "github.com/gravitl/netmaker/models"
9 | )
10 |
11 | var token string
12 |
13 | const node_id = "testnode"
14 | const net_id = "test"
15 | const key_name = "test"
16 | const node_mac = "01:02:03:04:05:06"
17 |
18 | func CreateTestData(t *testing.T, createNode bool) {
19 | c, err := NewClient(&host, &user, &pass)
20 | if err != nil {
21 | t.Fatal(err)
22 | }
23 |
24 | network := &models.Network{
25 | AddressRange: "10.102.0.0/24",
26 | LocalRange: "",
27 | IsLocal: "no",
28 | IsDualStack: "",
29 | AddressRange6: "",
30 | DefaultUDPHolePunch: "yes",
31 | NetID: net_id,
32 | }
33 | got, err := c.CreateNetwork(*network)
34 | if err != nil {
35 | t.Fatal(err)
36 | }
37 | key := &models.AccessKey{
38 | Name: key_name,
39 | Uses: 10,
40 | }
41 | accessKey, err := c.CreateKey(got.NetID, *key)
42 | token = accessKey.Value
43 | // token = accessKey.
44 | if err != nil {
45 | t.Fatal(err)
46 | }
47 | if createNode {
48 | node := models.Node{
49 | AccessKey: token,
50 | PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: node_id, Endpoint: "10.0.0.1", MacAddress: node_mac, Password: "password", Network: net_id,
51 | }
52 | _, err := c.CreateNetworkNode(network.NetID, node)
53 | if err != nil {
54 | t.Fatal(err)
55 | }
56 | }
57 | t.Cleanup(func() {
58 | err := CleanTestData()
59 | if err != nil {
60 | t.Fatal(err)
61 | }
62 | })
63 | }
64 |
65 | func CleanTestData() error {
66 | c, err := NewClient(&host, &user, &pass)
67 | if err != nil {
68 | return err
69 | }
70 | err = c.DeleteNetworkNode(net_id, node_mac)
71 | if err != nil {
72 | return err
73 | }
74 | err = c.DeleteKey(net_id, key_name)
75 | if err != nil {
76 | return err
77 | }
78 | return c.DeleteNetwork(net_id)
79 | }
80 | func TestClient_CreateNetworkNode(t *testing.T) {
81 | CreateTestData(t, false)
82 | // if err != nil {
83 | // t.Errorf("Client.CreateNetworkNode() error = %v", err)
84 |
85 | node := models.Node{
86 | AccessKey: token,
87 | PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: node_id, Endpoint: "10.0.0.1", MacAddress: node_mac, Password: "password", Network: net_id,
88 | }
89 | type fields struct {
90 | HostURL string
91 | HTTPClient *http.Client
92 | Token string
93 | Auth AuthStruct
94 | }
95 | type args struct {
96 | networkID string
97 | node models.Node
98 | }
99 | tests := []struct {
100 | name string
101 | fields fields
102 | args args
103 | want *models.Node
104 | wantErr bool
105 | }{
106 | {
107 |
108 | name: "Create Network Node",
109 | fields: fields{
110 | HostURL: host,
111 | HTTPClient: &http.Client{},
112 | Auth: AuthStruct{
113 | Username: user,
114 | Password: pass,
115 | },
116 | },
117 | args: args{
118 | networkID: net_id,
119 | node: node,
120 | },
121 | want: &node,
122 | wantErr: false,
123 | },
124 | }
125 | for _, tt := range tests {
126 | t.Run(tt.name, func(t *testing.T) {
127 | c, err := NewClient(&host, &user, &pass)
128 | if err != nil {
129 | t.Errorf("Client.CreateNetworkNode() error = %v", err)
130 | }
131 | got, err := c.CreateNetworkNode(tt.args.networkID, tt.args.node)
132 | if (err != nil) != tt.wantErr {
133 | t.Errorf("Client.CreateNetworkNode() error = %v, wantErr %v", err, tt.wantErr)
134 | return
135 | }
136 | if !reflect.DeepEqual(got.Network, net_id) {
137 | t.Errorf("Client.CreateNetworkNode() = %v, want %v", got.Network, net_id)
138 | }
139 | })
140 | }
141 | }
142 |
143 | func TestClient_GetNodes(t *testing.T) {
144 | CreateTestData(t, true)
145 | type fields struct {
146 | HostURL string
147 | HTTPClient *http.Client
148 | Token string
149 | Auth AuthStruct
150 | }
151 | tests := []struct {
152 | name string
153 | fields fields
154 | want []models.Node
155 | wantErr bool
156 | }{
157 | {
158 | name: "Get Nodes",
159 | fields: fields{
160 | HostURL: host,
161 | HTTPClient: &http.Client{},
162 | Auth: AuthStruct{
163 | Username: user,
164 | Password: pass,
165 | },
166 | },
167 | want: []models.Node{},
168 | wantErr: false,
169 | },
170 | }
171 | for _, tt := range tests {
172 | t.Run(tt.name, func(t *testing.T) {
173 | c, err := NewClient(&host, &user, &pass)
174 | if err != nil {
175 | t.Errorf("Client.NetClient() error = %v", err)
176 | }
177 | got, err := c.GetNodes()
178 | if (err != nil) != tt.wantErr {
179 | t.Errorf("Client.GetNodes() error = %v, wantErr %v", err, tt.wantErr)
180 | return
181 | }
182 | if !reflect.DeepEqual(got[0].Name, "testnode") {
183 | t.Errorf("Client.GetNodes() = %v, want %v", got[0].Name, "testnode")
184 | }
185 | })
186 | }
187 | }
188 |
189 | func TestClient_GetNetworkNodes(t *testing.T) {
190 | CreateTestData(t, true)
191 |
192 | type fields struct {
193 | HostURL string
194 | HTTPClient *http.Client
195 | Token string
196 | Auth AuthStruct
197 | }
198 | type args struct {
199 | networkID string
200 | }
201 | tests := []struct {
202 | name string
203 | fields fields
204 | args args
205 | want []models.Node
206 | wantErr bool
207 | }{
208 | {
209 | name: "Get Nodes Network",
210 | fields: fields{
211 | HostURL: host,
212 | HTTPClient: &http.Client{},
213 | Auth: AuthStruct{
214 | Username: user,
215 | Password: pass,
216 | },
217 | },
218 | args: args{
219 | networkID: net_id,
220 | },
221 | want: []models.Node{},
222 | wantErr: false,
223 | },
224 | }
225 | for _, tt := range tests {
226 | t.Run(tt.name, func(t *testing.T) {
227 | c, err := NewClient(&host, &user, &pass)
228 | if err != nil {
229 | t.Errorf("Client.NetClient() error = %v", err)
230 | }
231 | got, err := c.GetNetworkNodes(tt.args.networkID)
232 | if (err != nil) != tt.wantErr {
233 | t.Errorf("Client.GetNodes() error = %v, wantErr %v", err, tt.wantErr)
234 | return
235 | }
236 | if !reflect.DeepEqual(got[0].Name, "testnode") {
237 | t.Errorf("Client.GetNodes() = %v, want %v", got[0].Name, "testnode")
238 | }
239 | })
240 | }
241 | }
242 |
243 | func TestClient_GetNode(t *testing.T) {
244 | CreateTestData(t, true)
245 | type fields struct {
246 | HostURL string
247 | HTTPClient *http.Client
248 | Token string
249 | Auth AuthStruct
250 | }
251 | type args struct {
252 | networkID string
253 | mac string
254 | }
255 | tests := []struct {
256 | name string
257 | fields fields
258 | args args
259 | want models.Node
260 | wantErr bool
261 | }{
262 | {
263 | name: "Get Node",
264 | fields: fields{},
265 | args: args{
266 | networkID: net_id,
267 | mac: node_mac,
268 | },
269 | want: models.Node{
270 | Name: node_id,
271 | },
272 | wantErr: false,
273 | },
274 | }
275 | for _, tt := range tests {
276 | t.Run(tt.name, func(t *testing.T) {
277 | c, err := NewClient(&host, &user, &pass)
278 | if err != nil {
279 | t.Errorf("Client.NetClient() error = %v", err)
280 | }
281 | got, err := c.GetNode(tt.args.networkID, tt.args.mac)
282 | if (err != nil) != tt.wantErr {
283 | t.Errorf("Client.GetNode() error = %v, wantErr %v", err, tt.wantErr)
284 | return
285 | }
286 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
287 | t.Errorf("Client.GetNode() = %v, want %v", got, tt.want)
288 | }
289 | })
290 | }
291 | }
292 |
293 | func TestClient_GetNetworkIngress(t *testing.T) {
294 | CreateTestData(t, true)
295 | type fields struct {
296 | HostURL string
297 | HTTPClient *http.Client
298 | Token string
299 | Auth AuthStruct
300 | }
301 | type args struct {
302 | networkID string
303 | }
304 | tests := []struct {
305 | name string
306 | fields fields
307 | args args
308 | want []models.Node
309 | wantErr bool
310 | }{
311 | {
312 | name: "Get Network Ingress",
313 | fields: fields{},
314 | args: args{
315 | networkID: net_id,
316 | },
317 | want: []models.Node{},
318 | wantErr: false,
319 | },
320 | }
321 | for _, tt := range tests {
322 | t.Run(tt.name, func(t *testing.T) {
323 | c, err := NewClient(&host, &user, &pass)
324 | if err != nil {
325 | t.Errorf("Client.NetClient() error = %v", err)
326 | return
327 | }
328 | got, err := c.GetNetworkIngress(tt.args.networkID)
329 | if (err != nil) != tt.wantErr {
330 | t.Errorf("Client.GetNetworkIngress() error = %v, wantErr %v", err, tt.wantErr)
331 | return
332 | }
333 | if !reflect.DeepEqual(got, tt.want) {
334 | t.Errorf("Client.GetNetworkIngress() = %v, want %v", got, tt.want)
335 | }
336 | })
337 | }
338 | }
339 |
340 | func TestClient_CreateIngress(t *testing.T) {
341 | CreateTestData(t, true)
342 | type fields struct {
343 | HostURL string
344 | HTTPClient *http.Client
345 | Token string
346 | Auth AuthStruct
347 | }
348 | type args struct {
349 | networkID string
350 | mac string
351 | }
352 | tests := []struct {
353 | name string
354 | fields fields
355 | args args
356 | want *models.Node
357 | wantErr bool
358 | }{
359 | {
360 | name: "Create Ingress",
361 | fields: fields{},
362 | args: args{
363 | networkID: net_id,
364 | mac: node_mac,
365 | },
366 | want: &models.Node{
367 | Name: node_id,
368 | },
369 | wantErr: false,
370 | },
371 | }
372 | for _, tt := range tests {
373 | t.Run(tt.name, func(t *testing.T) {
374 | c, err := NewClient(&host, &user, &pass)
375 | if err != nil {
376 | t.Errorf("Client.NetClient() error = %v", err)
377 | }
378 | got, err := c.CreateIngress(tt.args.networkID, tt.args.mac)
379 | if (err != nil) != tt.wantErr {
380 | t.Errorf("Client.CreateIngress() error = %v, wantErr %v", err, tt.wantErr)
381 | return
382 | }
383 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
384 | t.Errorf("Client.CreateIngress() = %v, want %v", got, tt.want.Name)
385 | }
386 | })
387 | }
388 | }
389 |
390 | func TestClient_DeleteIngress(t *testing.T) {
391 | CreateTestData(t, true)
392 | type fields struct {
393 | HostURL string
394 | HTTPClient *http.Client
395 | Token string
396 | Auth AuthStruct
397 | }
398 | type args struct {
399 | networkID string
400 | mac string
401 | }
402 | tests := []struct {
403 | name string
404 | fields fields
405 | args args
406 | want *models.Node
407 | wantErr bool
408 | }{
409 | {
410 | name: "Delete Ingress",
411 | fields: fields{},
412 | args: args{
413 | networkID: net_id,
414 | mac: node_mac,
415 | },
416 | want: &models.Node{
417 | Name: node_id,
418 | },
419 | wantErr: false,
420 | },
421 | }
422 | for _, tt := range tests {
423 | t.Run(tt.name, func(t *testing.T) {
424 | c, err := NewClient(&host, &user, &pass)
425 | if err != nil {
426 | t.Errorf("Client.NetClient() error = %v", err)
427 | }
428 | _, err = c.CreateIngress(tt.args.networkID, tt.args.mac)
429 | if err != nil {
430 | t.Errorf("Create Ingress() error = %v", err)
431 | }
432 | got, err := c.DeleteIngress(tt.args.networkID, tt.args.mac)
433 | if (err != nil) != tt.wantErr {
434 | t.Errorf("Client.DeleteIngress() error = %v, wantErr %v", err, tt.wantErr)
435 | return
436 | }
437 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
438 | t.Errorf("Client.DeleteIngress() = %v, want %v", got.Name, tt.want.Name)
439 | }
440 | })
441 | }
442 | }
443 |
444 | func TestClient_CreateEgress(t *testing.T) {
445 | CreateTestData(t, true)
446 | type fields struct {
447 | HostURL string
448 | HTTPClient *http.Client
449 | Token string
450 | Auth AuthStruct
451 | }
452 | type args struct {
453 | networkID string
454 | mac string
455 | egress *models.EgressGatewayRequest
456 | }
457 | tests := []struct {
458 | name string
459 | fields fields
460 | args args
461 | want *models.Node
462 | wantErr bool
463 | }{
464 | {
465 | name: "Get Network Egress",
466 | fields: fields{},
467 | args: args{
468 | networkID: net_id,
469 | mac: node_mac,
470 | egress: &models.EgressGatewayRequest{
471 | Interface: "eth0",
472 | Ranges: []string{"0.0.0.0./0"},
473 | },
474 | },
475 | want: &models.Node{
476 | Name: node_id,
477 | },
478 | wantErr: false,
479 | },
480 | }
481 | for _, tt := range tests {
482 | t.Run(tt.name, func(t *testing.T) {
483 | c, err := NewClient(&host, &user, &pass)
484 | if err != nil {
485 | t.Errorf("Client.NetClient() error = %v", err)
486 | }
487 | got, err := c.CreateEgress(tt.args.networkID, tt.args.mac, tt.args.egress)
488 | if (err != nil) != tt.wantErr {
489 | t.Errorf("Client.CreateEgress() error = %v, wantErr %v", err, tt.wantErr)
490 | return
491 | }
492 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
493 | t.Errorf("Client.CreateEgress() = %v, want %v", got, tt.want)
494 | }
495 | })
496 | }
497 | }
498 |
499 | func TestClient_GetNetworkEgress(t *testing.T) {
500 | CreateTestData(t, true)
501 | type fields struct {
502 | HostURL string
503 | HTTPClient *http.Client
504 | Token string
505 | Auth AuthStruct
506 | }
507 | type args struct {
508 | networkID string
509 | }
510 | tests := []struct {
511 | name string
512 | fields fields
513 | args args
514 | want string
515 | wantErr bool
516 | }{
517 | {
518 | name: "Get Network Egress",
519 | fields: fields{},
520 | args: args{
521 | networkID: net_id,
522 | },
523 | want: "yes",
524 | wantErr: false,
525 | },
526 | }
527 | egress := &models.EgressGatewayRequest{
528 | Interface: "eth0",
529 | Ranges: []string{"0.0.0.0./0"},
530 | }
531 | for _, tt := range tests {
532 | t.Run(tt.name, func(t *testing.T) {
533 | c, err := NewClient(&host, &user, &pass)
534 | if err != nil {
535 | t.Errorf("Client.NetClient() error = %v", err)
536 | }
537 | _, err = c.CreateEgress(tt.args.networkID, node_mac, egress)
538 | if err != nil {
539 | t.Errorf("Create Egress error = %v", err)
540 | }
541 | got, err := c.GetNetworkEgress(tt.args.networkID)
542 | if (err != nil) != tt.wantErr {
543 | t.Errorf("Client.GetNetworkEgress() error = %v, wantErr %v", err, tt.wantErr)
544 | return
545 | }
546 | if !reflect.DeepEqual(got[0].IsEgressGateway, tt.want) {
547 | t.Errorf("Client.GetNetworkEgress() = %v, want %v", got, tt.want)
548 | }
549 | })
550 | }
551 | }
552 |
553 | func TestClient_DeleteEgress(t *testing.T) {
554 | CreateTestData(t, true)
555 | type fields struct {
556 | HostURL string
557 | HTTPClient *http.Client
558 | Token string
559 | Auth AuthStruct
560 | }
561 | type args struct {
562 | networkID string
563 | mac string
564 | }
565 | tests := []struct {
566 | name string
567 | fields fields
568 | args args
569 | want *models.Node
570 | wantErr bool
571 | }{
572 | {
573 | name: "Delete Network Egress",
574 | fields: fields{},
575 | args: args{
576 | networkID: net_id,
577 | mac: node_mac,
578 | },
579 | want: &models.Node{
580 | Name: node_id,
581 | },
582 | wantErr: false,
583 | },
584 | }
585 | for _, tt := range tests {
586 | t.Run(tt.name, func(t *testing.T) {
587 | c, err := NewClient(&host, &user, &pass)
588 | if err != nil {
589 | t.Errorf("Client.NetClient() error = %v", err)
590 | }
591 | egress := &models.EgressGatewayRequest{
592 | Interface: "eth0",
593 | Ranges: []string{"0.0.0.0./0"},
594 | }
595 | _, err = c.CreateEgress(tt.args.networkID, tt.args.mac, egress)
596 | if err != nil {
597 | t.Errorf("Client.NetClient() error = %v", err)
598 | }
599 | got, err := c.DeleteEgress(tt.args.networkID, tt.args.mac)
600 | if (err != nil) != tt.wantErr {
601 | t.Errorf("Client.DeleteEgress() error = %v, wantErr %v", err, tt.wantErr)
602 | return
603 | }
604 | if !reflect.DeepEqual(got.Name, tt.want.Name) {
605 | t.Errorf("Client.DeleteEgress() = %v, want %v", got, tt.want)
606 | }
607 | })
608 | }
609 | }
610 |
611 | func TestClient_DeleteNetworkNode(t *testing.T) {
612 | CreateTestData(t, true)
613 | type fields struct {
614 | HostURL string
615 | HTTPClient *http.Client
616 | Token string
617 | Auth AuthStruct
618 | }
619 | type args struct {
620 | networkID string
621 | mac string
622 | }
623 | tests := []struct {
624 | name string
625 | fields fields
626 | args args
627 | wantErr bool
628 | }{
629 | {
630 | name: "Delete Network Node",
631 | fields: fields{},
632 | args: args{
633 | networkID: net_id,
634 | mac: node_mac,
635 | },
636 | wantErr: false,
637 | },
638 | }
639 | for _, tt := range tests {
640 | t.Run(tt.name, func(t *testing.T) {
641 | c, err := NewClient(&host, &user, &pass)
642 | if err != nil {
643 | t.Errorf("Client.NetClient() error = %v", err)
644 | }
645 | if err := c.DeleteNetworkNode(tt.args.networkID, tt.args.mac); (err != nil) != tt.wantErr {
646 | t.Errorf("Client.DeleteNetworkNode() error = %v, wantErr %v", err, tt.wantErr)
647 | }
648 | })
649 | }
650 | }
651 |
--------------------------------------------------------------------------------
/helper/user.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 | "strings"
8 |
9 | "github.com/gravitl/netmaker/models"
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11 | )
12 |
13 | func (c *Client) CreateAdmin(user models.User) error {
14 |
15 | rb, err := json.Marshal(user)
16 | if err != nil {
17 | return err
18 | }
19 |
20 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/users/adm/createadmin", c.HostURL), strings.NewReader(string(rb)))
21 | if err != nil {
22 | return err
23 | }
24 |
25 | _, err = c.doRequest(req)
26 | if err != nil {
27 | return err
28 | }
29 | return nil
30 |
31 | }
32 |
33 | func (c *Client) CheckAdmin() (bool, error) {
34 | admin := false
35 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/users/adm/hasadmin", c.HostURL), nil)
36 | if err != nil {
37 | return admin, err
38 | }
39 |
40 | body, err := c.doRequest(req)
41 | if err != nil {
42 | return admin, err
43 | }
44 |
45 | err = json.Unmarshal(body, &admin)
46 | if err != nil {
47 | return admin, err
48 | }
49 |
50 | return admin, nil
51 | }
52 |
53 | func (c *Client) CreateUser(user models.User) error {
54 |
55 | rb, err := json.Marshal(user)
56 | if err != nil {
57 | return err
58 | }
59 |
60 | req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/users/%s", c.HostURL, user.UserName), strings.NewReader(string(rb)))
61 | if err != nil {
62 | return err
63 | }
64 |
65 | _, err = c.doRequest(req)
66 | if err != nil {
67 | return err
68 | }
69 | return nil
70 | }
71 |
72 | func (c *Client) DeleteUser(username string) error {
73 |
74 | req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/users/%s", c.HostURL, username), nil)
75 | if err != nil {
76 | return err
77 | }
78 |
79 | _, err = c.doRequest(req)
80 | if err != nil {
81 | return err
82 | }
83 | return nil
84 |
85 | }
86 |
87 | func (c *Client) UpdateUser(user models.User) error {
88 | rb, err := json.Marshal(user)
89 | if err != nil {
90 | return err
91 | }
92 |
93 | req, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/users/%s", c.HostURL, user.UserName), strings.NewReader(string(rb)))
94 | if err != nil {
95 | return err
96 | }
97 |
98 | _, err = c.doRequest(req)
99 | if err != nil {
100 | return err
101 | }
102 | return nil
103 | }
104 |
105 | func CreateUserSchema() map[string]*schema.Schema {
106 | return map[string]*schema.Schema{
107 | "username": {
108 | Type: schema.TypeString,
109 | Required: true,
110 | },
111 | "password": {
112 | Type: schema.TypeString,
113 | Sensitive: true,
114 | Optional: true,
115 | },
116 | "networks": {
117 | Type: schema.TypeList,
118 | Computed: true,
119 | Optional: true,
120 | Elem: &schema.Schema{
121 | Type: schema.TypeString,
122 | },
123 | },
124 | }
125 | }
126 |
127 | func (c *Client) GetUser(username string) (*models.User, error) {
128 |
129 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/users/%s", c.HostURL, username), nil)
130 | if err != nil {
131 | return nil, err
132 | }
133 |
134 | body, err := c.doRequest(req)
135 | if err != nil {
136 | return nil, err
137 | }
138 |
139 | user := models.User{}
140 | err = json.Unmarshal(body, &user)
141 | if err != nil {
142 | return nil, err
143 | }
144 |
145 | return &user, nil
146 | }
147 |
148 | func CreateUserFromSchemaData(d *schema.ResourceData) *models.User {
149 | user := &models.User{}
150 | user.UserName = d.Get("username").(string)
151 | user.Password = d.Get("password").(string)
152 | networks := d.Get("network")
153 | if networks != nil {
154 | user.Networks = d.Get("network").([]string)
155 | } else {
156 | user.Networks = []string{}
157 | }
158 |
159 | return user
160 | }
161 |
162 | func (c *Client) CreateUserFromSchema(d *schema.ResourceData) (*models.User, error) {
163 | user := CreateUserFromSchemaData(d)
164 | return user, c.CreateUser(*user)
165 | }
166 |
167 | func (c *Client) CreateAdminUserFromSchema(d *schema.ResourceData) (*models.User, error) {
168 | user := CreateUserFromSchemaData(d)
169 | return user, c.CreateAdmin(*user)
170 | }
171 |
172 | func SetUserSchemaData(d *schema.ResourceData, user *models.User) error {
173 | d.Set("username", user.UserName)
174 | d.Set("network", user.Networks)
175 | return nil
176 | }
177 |
178 | func (c *Client) UpdateUserFromSchema(d *schema.ResourceData) (*models.User, error) {
179 | user := CreateUserFromSchemaData(d)
180 | return user, c.UpdateUser(*user)
181 | }
182 |
--------------------------------------------------------------------------------
/helper/user_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "net/http"
5 | "testing"
6 |
7 | "github.com/gravitl/netmaker/models"
8 | )
9 |
10 | func TestClient_CreateAdmin(t *testing.T) {
11 | type fields struct {
12 | HostURL string
13 | HTTPClient *http.Client
14 | Token string
15 | Auth AuthStruct
16 | }
17 | type args struct {
18 | user models.User
19 | }
20 | tests := []struct {
21 | name string
22 | fields fields
23 | args args
24 | wantErr bool
25 | }{
26 | // TODO: Add test cases.
27 | {
28 | name: "Create admin user",
29 | fields: fields{
30 | HostURL: host,
31 | HTTPClient: &http.Client{},
32 | Token: "",
33 | Auth: AuthStruct{
34 | Username: user,
35 | Password: pass,
36 | },
37 | },
38 | args: args{
39 | user: models.User{
40 | UserName: "admin_test",
41 | Password: pass,
42 | },
43 | },
44 | wantErr: false,
45 | },
46 | }
47 | for _, tt := range tests {
48 | t.Run(tt.name, func(t *testing.T) {
49 | c := &Client{
50 | HostURL: "http://localhost:8081",
51 | HTTPClient: tt.fields.HTTPClient,
52 | Token: tt.fields.Token,
53 | Auth: tt.fields.Auth,
54 | }
55 | if err := c.CreateAdmin(tt.args.user); (err != nil) != tt.wantErr {
56 | t.Errorf("Client.CreateAdmin() error = %v, wantErr %v", err, tt.wantErr)
57 | }
58 | })
59 | }
60 | }
61 |
62 | func TestClient_CheckAdmin(t *testing.T) {
63 | type fields struct {
64 | HostURL string
65 | HTTPClient *http.Client
66 | Token string
67 | Auth AuthStruct
68 | }
69 | tests := []struct {
70 | name string
71 | fields fields
72 | want bool
73 | wantErr bool
74 | }{
75 | // TODO: Add test cases.
76 | {
77 | name: "Check admin user",
78 | fields: fields{
79 | HostURL: host,
80 | HTTPClient: &http.Client{},
81 | Token: "",
82 | Auth: AuthStruct{
83 | Username: user,
84 | Password: pass,
85 | },
86 | },
87 | want: true,
88 | wantErr: false,
89 | },
90 | }
91 | for _, tt := range tests {
92 | t.Run(tt.name, func(t *testing.T) {
93 | c := &Client{
94 | HostURL: tt.fields.HostURL,
95 | HTTPClient: tt.fields.HTTPClient,
96 | Token: tt.fields.Token,
97 | Auth: tt.fields.Auth,
98 | }
99 | got, err := c.CheckAdmin()
100 | if (err != nil) != tt.wantErr {
101 | t.Errorf("Client.CheckAdmin() error = %v, wantErr %v", err, tt.wantErr)
102 | return
103 | }
104 | if got != tt.want {
105 | t.Errorf("Client.CheckAdmin() = %v, want %v", got, tt.want)
106 | }
107 | })
108 | }
109 | }
110 |
111 | func TestClient_CreateUser(t *testing.T) {
112 | type fields struct {
113 | HostURL string
114 | HTTPClient *http.Client
115 | Token string
116 | Auth AuthStruct
117 | }
118 | type args struct {
119 | user models.User
120 | }
121 | tests := []struct {
122 | name string
123 | fields fields
124 | args args
125 | wantErr bool
126 | }{
127 | // TODO: Add test cases.
128 | {
129 | name: "Create user",
130 | fields: fields{
131 | HostURL: host,
132 | HTTPClient: &http.Client{},
133 | Token: "",
134 | Auth: AuthStruct{
135 | Username: user,
136 | Password: pass,
137 | },
138 | },
139 | args: args{
140 | user: models.User{
141 | UserName: "user_test",
142 | Password: pass,
143 | },
144 | },
145 | wantErr: false,
146 | },
147 | }
148 | for _, tt := range tests {
149 | t.Run(tt.name, func(t *testing.T) {
150 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
151 |
152 | if err := c.CreateUser(tt.args.user); (err != nil) != tt.wantErr {
153 | t.Errorf("Client.CreateUser() error = %v, wantErr %v", err, tt.wantErr)
154 | }
155 | })
156 | }
157 | }
158 |
159 | func TestClient_DeleteUser(t *testing.T) {
160 | type fields struct {
161 | HostURL string
162 | HTTPClient *http.Client
163 | Token string
164 | Auth AuthStruct
165 | }
166 | type args struct {
167 | user models.User
168 | }
169 | tests := []struct {
170 | name string
171 | fields fields
172 | args args
173 | wantErr bool
174 | }{
175 | // TODO: Add test cases.
176 | {
177 | name: "Delete user",
178 | fields: fields{
179 | HostURL: host,
180 | HTTPClient: &http.Client{},
181 | Token: "",
182 | Auth: AuthStruct{
183 | Username: user,
184 | Password: pass,
185 | },
186 | },
187 | args: args{
188 | user: models.User{
189 | UserName: "user_test",
190 | Password: pass,
191 | },
192 | },
193 | wantErr: false,
194 | },
195 | }
196 | for _, tt := range tests {
197 | t.Run(tt.name, func(t *testing.T) {
198 | c, _ := NewClient(&tt.fields.HostURL, &tt.fields.Auth.Username, &tt.fields.Auth.Password)
199 |
200 | if err := c.DeleteUser(tt.args.user.UserName); (err != nil) != tt.wantErr {
201 | t.Errorf("Client.DeleteUser() error = %v, wantErr %v", err, tt.wantErr)
202 | }
203 | })
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/internal/provider/data_source_access_key.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/madacluster/netmaker-terraform-provider/helper"
11 | )
12 |
13 | func AddIdAccessKeySchema() map[string]*schema.Schema {
14 | result := helper.CreateAccessKeySchema()
15 | result["id"] = &schema.Schema{
16 | Type: schema.TypeString,
17 | Required: true,
18 | }
19 | return result
20 | }
21 |
22 | func dataSourceAccessKey() *schema.Resource {
23 | return &schema.Resource{
24 | // This description is used by the documentation generator and the language server.
25 | Description: "AccessKey Data source in the Terraform provider Netmaker.",
26 |
27 | ReadContext: dataSourceAccessKeyRead,
28 |
29 | Schema: helper.CreateAccessKeySchema(),
30 | }
31 | }
32 |
33 | func dataSourceAccessKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
34 | // use the meta value to retrieve your client from the provider configure method
35 | client := meta.(*helper.Client)
36 | accessKeyID := d.Get("name").(string)
37 | netID := d.Get("netid").(string)
38 |
39 | // Warning or errors can be collected in a slice type
40 | var diags diag.Diagnostics
41 |
42 | key, err := client.GetKey(accessKeyID, netID)
43 | if err != nil {
44 | return diag.FromErr(err)
45 | }
46 | err = helper.SetAccessKeySchemaData(d, key, netID)
47 |
48 | if err != nil {
49 | return diag.FromErr(err)
50 | }
51 | // always run
52 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
53 |
54 | return diags
55 | }
56 |
--------------------------------------------------------------------------------
/internal/provider/data_source_access_key_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccDataSourceAccessKey(t *testing.T) {
11 | // t.Skip("data source not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccDataSourceAccessKey,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "data.netmaker_access_key.foo", "netid", regexp.MustCompile("^netmakertest")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccDataSourceAccessKey = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | data "netmaker_access_key" "foo" {
35 | name = "netmakertest"
36 | netid = "netmakertest"
37 | }
38 | `
39 |
--------------------------------------------------------------------------------
/internal/provider/data_source_network.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/madacluster/netmaker-terraform-provider/helper"
11 | )
12 |
13 | func dataSourceNetwork() *schema.Resource {
14 | return &schema.Resource{
15 | // This description is used by the documentation generator and the language server.
16 | Description: "models.Network Data source in the Terraform provider Netmaker.",
17 |
18 | ReadContext: dataSourceNetworkRead,
19 |
20 | Schema: helper.CreateNetworkSchema(),
21 | }
22 | }
23 |
24 | func dataSourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
25 | // use the meta value to retrieve your client from the provider configure method
26 | client := meta.(*helper.Client)
27 | networkID := d.Get("netid").(string)
28 | // idFromAPI := "my-id"
29 | // d.SetId(idFromAPI)
30 |
31 | // Warning or errors can be collected in a slice type
32 | var diags diag.Diagnostics
33 |
34 | network, err := client.GetNetwork(networkID)
35 | if err != nil {
36 | return diag.FromErr(err)
37 | }
38 | err = helper.SetNetworkSchemaData(d, network)
39 |
40 | if err != nil {
41 | return diag.FromErr(err)
42 | }
43 | // always run
44 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
45 |
46 | return diags
47 | }
48 |
--------------------------------------------------------------------------------
/internal/provider/data_source_network_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccDataSourceNetwork(t *testing.T) {
11 | // t.Skip("data source not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccDataSourceNetwork,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "data.netmaker_network.foo", "netid", regexp.MustCompile("^netmakertest")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccDataSourceNetwork = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | data "netmaker_network" "foo" {
35 | netid = "netmakertest"
36 | addressrange = "10.100.10.0/24"
37 | }
38 | `
39 |
--------------------------------------------------------------------------------
/internal/provider/data_source_networks.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/madacluster/netmaker-terraform-provider/helper"
11 | )
12 |
13 | func dataSourceNetworks() *schema.Resource {
14 | return &schema.Resource{
15 | // This description is used by the documentation generator and the language server.
16 | Description: "models.Network Data source in the Terraform provider Netmaker.",
17 |
18 | ReadContext: dataSourceNetworksRead,
19 |
20 | Schema: map[string]*schema.Schema{
21 | "networks": {
22 | Type: schema.TypeList,
23 | Computed: true,
24 | Elem: &schema.Resource{
25 | Schema: helper.CreateNetworkSchema(),
26 | },
27 | },
28 | },
29 | }
30 | }
31 |
32 | func dataSourceNetworksRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
33 | // use the meta value to retrieve your client from the provider configure method
34 | client := meta.(*helper.Client)
35 |
36 | // idFromAPI := "my-id"
37 | // d.SetId(idFromAPI)
38 |
39 | // Warning or errors can be collected in a slice type
40 | var diags diag.Diagnostics
41 |
42 | networks, err := client.GetNetworks()
43 | networksFlatten := helper.FlattenNetworksData(&networks)
44 |
45 | if err != nil {
46 | return diag.FromErr(err)
47 | }
48 |
49 | if err := d.Set("networks", networksFlatten); err != nil {
50 | return diag.FromErr(err)
51 | }
52 |
53 | // always run
54 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
55 |
56 | return diags
57 | }
58 |
--------------------------------------------------------------------------------
/internal/provider/data_source_networks_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccDataSourceNetworks(t *testing.T) {
11 | // t.Skip("data source not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccDataSourceNetworks,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "data.netmaker_networks.foo", "networks.0.netid", regexp.MustCompile("^netmakertest")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccDataSourceNetworks = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | data "netmaker_networks" "foo" {
35 | }
36 | `
37 |
--------------------------------------------------------------------------------
/internal/provider/data_source_node.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/madacluster/netmaker-terraform-provider/helper"
11 | )
12 |
13 | func dataSourceNode() *schema.Resource {
14 | return &schema.Resource{
15 | // This description is used by the documentation generator and the language server.
16 | Description: "Node Data source in the Terraform provider Netmaker.",
17 |
18 | ReadContext: dataSourceNodeRead,
19 |
20 | Schema: helper.CreateNodeDataSchema(),
21 | }
22 | }
23 |
24 | func dataSourceNodeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
25 | // use the meta value to retrieve your client from the provider configure method
26 | client := meta.(*helper.Client)
27 | mac := d.Get("mac").(string)
28 | netID := d.Get("network_id").(string)
29 |
30 | // Warning or errors can be collected in a slice type
31 | var diags diag.Diagnostics
32 |
33 | node, err := client.GetNode(netID, mac)
34 | if err != nil {
35 | return diag.FromErr(err)
36 | }
37 | helper.SetNodeSchemaData(d, node, netID)
38 |
39 | // always run
40 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
41 |
42 | return diags
43 | }
44 |
--------------------------------------------------------------------------------
/internal/provider/data_source_node_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/gravitl/netmaker/models"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9 | "github.com/madacluster/netmaker-terraform-provider/helper"
10 | )
11 |
12 | var token string
13 |
14 | const node_id = "testnode"
15 | const net_id = "test"
16 | const key_name = "test"
17 | const node_mac = "01:02:03:04:05:06"
18 |
19 | var host = "http://localhost:8081"
20 | var pass = "mx4S6JsSg7JWcZ"
21 | var user = "admin"
22 |
23 | // const ipRange = "10.101.0.0/24"
24 |
25 | func CreateTestData(t *testing.T, createNode bool) {
26 | c, err := helper.NewClient(&host, &user, &pass)
27 | if err != nil {
28 | t.Fatal(err)
29 | }
30 |
31 | network := &models.Network{
32 | AddressRange: "10.102.0.0/24",
33 | LocalRange: "",
34 | IsLocal: "no",
35 | IsDualStack: "",
36 | AddressRange6: "",
37 | DefaultUDPHolePunch: "yes",
38 | NetID: net_id,
39 | }
40 | got, err := c.CreateNetwork(*network)
41 | if err != nil {
42 | t.Fatal(err)
43 | }
44 | key := &models.AccessKey{
45 | Name: key_name,
46 | Uses: 10,
47 | }
48 | accessKey, err := c.CreateKey(got.NetID, *key)
49 | // token = accessKey.
50 | if err != nil {
51 | t.Fatal(err)
52 | }
53 | token = accessKey.Value
54 | if createNode {
55 | node := models.Node{
56 | AccessKey: token,
57 | PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: node_id, Endpoint: "10.0.0.1", MacAddress: node_mac, Password: "password", Network: net_id,
58 | }
59 | _, err := c.CreateNetworkNode(network.NetID, node)
60 | if err != nil {
61 | t.Fatal(err)
62 | }
63 | }
64 | t.Cleanup(func() {
65 | err := CleanTestData()
66 | if err != nil {
67 | t.Fatal(err)
68 | }
69 | })
70 | }
71 |
72 | func CleanTestData() error {
73 | c, err := helper.NewClient(&host, &user, &pass)
74 | if err != nil {
75 | return err
76 | }
77 | err = c.DeleteNetworkNode(net_id, node_mac)
78 | if err != nil {
79 | return err
80 | }
81 | err = c.DeleteKey(net_id, key_name)
82 | if err != nil {
83 | return err
84 | }
85 | return c.DeleteNetwork(net_id)
86 | }
87 | func TestAccDataSourceNode(t *testing.T) {
88 | // t.Skip("data source not yet implemented, remove this once you add your own code")
89 | CreateTestData(t, true)
90 | resource.UnitTest(t, resource.TestCase{
91 | PreCheck: func() { testAccPreCheck(t) },
92 | ProviderFactories: providerFactories,
93 | Steps: []resource.TestStep{
94 | {
95 | Config: testAccDataSourceNode,
96 | Check: resource.ComposeTestCheckFunc(
97 | resource.TestMatchResourceAttr(
98 | "data.netmaker_node.foo", "name", regexp.MustCompile("^testnode")),
99 | ),
100 | },
101 | },
102 | })
103 | }
104 |
105 | const testAccDataSourceNode = `
106 | provider "netmaker" {
107 | username = "admin"
108 | password = "mx4S6JsSg7JWcZ"
109 | host = "http://localhost:8081"
110 | }
111 |
112 | data "netmaker_node" "foo" {
113 | network_id = "test"
114 | mac = "01:02:03:04:05:06"
115 | }
116 | `
117 |
--------------------------------------------------------------------------------
/internal/provider/provider.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8 | client "github.com/madacluster/netmaker-terraform-provider/helper"
9 | )
10 |
11 | func init() {
12 | // Set descriptions to support markdown syntax, this will be used in document generation
13 | // and the language server.
14 | schema.DescriptionKind = schema.StringMarkdown
15 |
16 | // Customize the content of descriptions when output. For example you can add defaults on
17 | // to the exported descriptions if present.
18 | // schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
19 | // desc := s.Description
20 | // if s.Default != nil {
21 | // desc += fmt.Sprintf(" Defaults to `%v`.", s.Default)
22 | // }
23 | // return strings.TrimSpace(desc)
24 | // }
25 | }
26 |
27 | func New(version string) func() *schema.Provider {
28 | return func() *schema.Provider {
29 | p := &schema.Provider{
30 | DataSourcesMap: map[string]*schema.Resource{
31 | "netmaker_networks": dataSourceNetworks(),
32 | "netmaker_network": dataSourceNetwork(),
33 | "netmaker_access_key": dataSourceAccessKey(),
34 | "netmaker_node": dataSourceNode(),
35 | },
36 | ResourcesMap: map[string]*schema.Resource{
37 | "netmaker_network": resourceNetwork(),
38 | "netmaker_user": resourceUser(),
39 | "netmaker_access_key": resourceAccessKey(),
40 | "netmaker_egress": resourceEgress(),
41 | "netmaker_ingress": resourceIngress(),
42 | },
43 | Schema: map[string]*schema.Schema{
44 | "username": {
45 | Type: schema.TypeString,
46 | Required: true,
47 | DefaultFunc: schema.EnvDefaultFunc("NETMAKER_USERNAME", nil),
48 | },
49 | "password": {
50 | Type: schema.TypeString,
51 | Required: true,
52 | Sensitive: true,
53 | DefaultFunc: schema.EnvDefaultFunc("NETMAKER_PASSWORD", nil),
54 | },
55 | "host": {
56 | Type: schema.TypeString,
57 | Required: true,
58 | Sensitive: true,
59 | DefaultFunc: schema.EnvDefaultFunc("NETMAKER_HOST", nil),
60 | },
61 | },
62 | }
63 |
64 | p.ConfigureContextFunc = configure(version, p)
65 |
66 | return p
67 | }
68 | }
69 |
70 | func configure(version string, p *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) {
71 | return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
72 | // Setup a models.User-Agent for your API client (replace the provider name for yours):
73 | // userAgent := p.UserAgent("terraform-provider-scaffolding", version)
74 | // TODO: myClient.UserAgent = userAgent
75 | username := d.Get("username").(string)
76 | password := d.Get("password").(string)
77 | host := d.Get("host").(string)
78 |
79 | // Warning or errors can be collected in a slice type
80 | var diags diag.Diagnostics
81 | if (username != "") && (password != "") && (host != "") {
82 | c, err := client.NewClient(&host, &username, &password)
83 | if err != nil {
84 | diags = append(diags, diag.Diagnostic{
85 | Severity: diag.Error,
86 | Summary: "Unable to create HashiCups client",
87 | Detail: "Unable to auth user for authenticated HashiCups client",
88 | })
89 | }
90 |
91 | return c, diags
92 | }
93 |
94 | c, err := client.NewClient(nil, nil, nil)
95 | if err != nil {
96 | diags = append(diags, diag.Diagnostic{
97 | Severity: diag.Error,
98 | Summary: "Unable to create HashiCups client",
99 | Detail: "Unable to auth user for authenticated HashiCups client",
100 | })
101 | }
102 |
103 | return c, diags
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/internal/provider/provider_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
7 | )
8 |
9 | // providerFactories are used to instantiate a provider during acceptance testing.
10 | // The factory function will be invoked for every Terraform CLI command executed
11 | // to create a provider server to which the CLI can reattach.
12 | var providerFactories = map[string]func() (*schema.Provider, error){
13 | "netmaker": func() (*schema.Provider, error) {
14 | return New("dev")(), nil
15 | },
16 | }
17 |
18 | func TestProvider(t *testing.T) {
19 | if err := New("dev")().InternalValidate(); err != nil {
20 | t.Fatalf("err: %s", err)
21 | }
22 | }
23 |
24 | func testAccPreCheck(t *testing.T) {
25 | // You can add code here to run prior to any test case execution, for example assertions
26 | // about the appropriate environment variables being set are common to see in a pre-check
27 | // function.
28 | }
29 |
--------------------------------------------------------------------------------
/internal/provider/resource_access_key.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/madacluster/netmaker-terraform-provider/helper"
11 | )
12 |
13 | func resourceAccessKey() *schema.Resource {
14 | return &schema.Resource{
15 | // This description is used by the documentation generator and the language server.
16 | Description: "Sample resource in the Terraform provider scaffolding.",
17 |
18 | CreateContext: resourceAccessKeyCreate,
19 | ReadContext: resourceAccessKeyRead,
20 | UpdateContext: resourceAccessKeyUpdate,
21 | DeleteContext: resourceAccessKeyDelete,
22 |
23 | Schema: addLastupdated(helper.CreateAccessKeySchema()),
24 | }
25 | }
26 |
27 | func resourceAccessKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
28 | // use the meta value to retrieve your client from the provider configure method
29 | // client := meta.(*apiClient)
30 | var diags diag.Diagnostics
31 |
32 | client := meta.(*helper.Client)
33 | netID := d.Get("netid").(string)
34 | key, err := client.CreateAccessKeyFromSchema(d, netID)
35 |
36 | if err != nil {
37 | return diag.Errorf("failed to create models.AccessKey: %s", err)
38 | }
39 |
40 | ID := fmt.Sprintf("%s-%s", netID, key.Name)
41 | d.SetId(ID)
42 | return diags
43 | }
44 |
45 | func resourceAccessKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
46 | // use the meta value to retrieve your client from the provider configure method
47 | client := meta.(*helper.Client)
48 | accessKeyID := d.Get("name").(string)
49 | netid := d.Get("netid").(string)
50 | // d.SetId(UserID)
51 |
52 | // Warning or errors can be collected in a slice type
53 | var diags diag.Diagnostics
54 |
55 | accessKey, err := client.GetKey(netid, accessKeyID)
56 | if err != nil {
57 | return diag.FromErr(err)
58 | }
59 |
60 | if helper.SetAccessKeySchemaData(d, accessKey, netid) != nil {
61 | return diag.FromErr(err)
62 | }
63 | return diags
64 | }
65 |
66 | func resourceAccessKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
67 | // use the meta value to retrieve your client from the provider configure method
68 | client := meta.(*helper.Client)
69 | if d.HasChanges("name") {
70 | networkID := d.Get("netid").(string)
71 | err := client.UpdateKeyFromSchema(d, networkID)
72 | if err != nil {
73 | return diag.FromErr(err)
74 | }
75 | d.Set("last_updated", time.Now().Format(time.RFC850))
76 | }
77 |
78 | return resourceAccessKeyRead(ctx, d, meta)
79 | }
80 |
81 | func resourceAccessKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
82 | // use the meta value to retrieve your client from the provider configure method
83 | // client := meta.(*apiClient)
84 | client := meta.(*helper.Client)
85 | var diags diag.Diagnostics
86 |
87 | networkID := d.Get("netid").(string)
88 | name := d.Get("name").(string)
89 | err := client.DeleteKey(networkID, name)
90 | if err != nil {
91 | return diag.FromErr(err)
92 | }
93 |
94 | // d.SetId("") is automatically called assuming delete returns no errors, but
95 | // it is added here for explicitness.
96 | d.SetId("")
97 |
98 | return diags
99 | }
100 |
--------------------------------------------------------------------------------
/internal/provider/resource_access_key_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccResourceAccessKey(t *testing.T) {
11 | // t.Skip("resource not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccResourceAccessKey,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "netmaker_access_key.foo", "netid", regexp.MustCompile("^test")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccResourceAccessKey = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | resource "netmaker_network" "foo" {
35 | netid = "test"
36 | addressrange = "10.100.10.0/24"
37 | }
38 | resource "netmaker_access_key" "foo" {
39 | depends_on = ["netmaker_network.foo"]
40 | netid = "test"
41 | uses = 10
42 | name = "test"
43 | }
44 | `
45 |
--------------------------------------------------------------------------------
/internal/provider/resource_egress.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | "github.com/madacluster/netmaker-terraform-provider/helper"
10 | )
11 |
12 | func resourceEgress() *schema.Resource {
13 | return &schema.Resource{
14 | // This description is used by the documentation generator and the language server.
15 | Description: "Sample resource in the Terraform provider scaffolding.",
16 |
17 | CreateContext: resourceEgressCreate,
18 | ReadContext: resourceEgressRead,
19 | UpdateContext: resourceEgressUpdate,
20 | DeleteContext: resourceEgressDelete,
21 |
22 | Schema: addLastupdated(helper.CreateEgressSchema()),
23 | }
24 | }
25 |
26 | func resourceEgressCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
27 | // use the meta value to retrieve your client from the provider configure method
28 | // client := meta.(*apiClient)
29 | var diags diag.Diagnostics
30 |
31 | client := meta.(*helper.Client)
32 | netID := d.Get("netid").(string)
33 | mac := d.Get("mac").(string)
34 | node, err := client.CreateEgressFromSchema(d, netID, mac)
35 |
36 | if err != nil {
37 | return diag.Errorf("failed to create models.Egress: %s", err)
38 | }
39 |
40 | ID := fmt.Sprintf("%s-%s", netID, node.MacAddress)
41 | d.SetId(ID)
42 | return diags
43 | }
44 |
45 | func resourceEgressRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
46 | // use the meta value to retrieve your client from the provider configure method
47 | client := meta.(*helper.Client)
48 | mac := d.Get("mac").(string)
49 | netid := d.Get("netid").(string)
50 | // d.SetId(UserID)
51 |
52 | // Warning or errors can be collected in a slice type
53 | var diags diag.Diagnostics
54 |
55 | node, err := client.GetNode(netid, mac)
56 | if err != nil {
57 | return diag.FromErr(err)
58 | }
59 | helper.SetEgressSchemaData(d, node, netid, mac)
60 |
61 | return diags
62 | }
63 |
64 | func resourceEgressUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
65 | // use the meta value to retrieve your client from the provider configure method
66 | resourceEgressDelete(ctx, d, meta)
67 |
68 | return resourceEgressCreate(ctx, d, meta)
69 | }
70 |
71 | func resourceEgressDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
72 | // use the meta value to retrieve your client from the provider configure method
73 | // client := meta.(*apiClient)
74 | client := meta.(*helper.Client)
75 | var diags diag.Diagnostics
76 |
77 | networkID := d.Get("netid").(string)
78 | mac := d.Get("mac").(string)
79 | _, err := client.DeleteEgress(networkID, mac)
80 | if err != nil {
81 | return diag.FromErr(err)
82 | }
83 |
84 | // d.SetId("") is automatically called assuming delete returns no errors, but
85 | // it is added here for explicitness.
86 | d.SetId("")
87 |
88 | return diags
89 | }
90 |
--------------------------------------------------------------------------------
/internal/provider/resource_egress_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccResourceEgress(t *testing.T) {
11 | // t.Skip("resource not yet implemented, remove this once you add your own code")
12 | CreateTestData(t, true)
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccResourceEgress,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "netmaker_egress.foo", "netid", regexp.MustCompile("^test")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccResourceEgress = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 |
35 | resource "netmaker_egress" "foo" {
36 | netid = "test"
37 | mac = "01:02:03:04:05:06"
38 | interface = "nm-test"
39 | ranges = ["0.0.0.0/0"]
40 | }
41 | `
42 |
--------------------------------------------------------------------------------
/internal/provider/resource_ingress.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | "github.com/madacluster/netmaker-terraform-provider/helper"
10 | )
11 |
12 | func resourceIngress() *schema.Resource {
13 | return &schema.Resource{
14 | // This description is used by the documentation generator and the language server.
15 | Description: "Sample resource in the Terraform provider scaffolding.",
16 |
17 | CreateContext: resourceIngressCreate,
18 | ReadContext: resourceIngressRead,
19 | UpdateContext: resourceIngressUpdate,
20 | DeleteContext: resourceIngressDelete,
21 |
22 | Schema: addLastupdated(helper.CreateIngressSchema()),
23 | }
24 | }
25 |
26 | func resourceIngressCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
27 | // use the meta value to retrieve your client from the provider configure method
28 | // client := meta.(*apiClient)
29 | var diags diag.Diagnostics
30 |
31 | client := meta.(*helper.Client)
32 | netID := d.Get("netid").(string)
33 | mac := d.Get("mac").(string)
34 | node, err := client.CreateIngress(netID, mac)
35 |
36 | if err != nil {
37 | return diag.Errorf("failed to create models.Ingress: %s", err)
38 | }
39 |
40 | ID := fmt.Sprintf("%s-%s", netID, node.MacAddress)
41 | d.SetId(ID)
42 | return diags
43 | }
44 |
45 | func resourceIngressRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
46 | // use the meta value to retrieve your client from the provider configure method
47 | client := meta.(*helper.Client)
48 | mac := d.Get("mac").(string)
49 | netid := d.Get("netid").(string)
50 | // d.SetId(UserID)
51 |
52 | // Warning or errors can be collected in a slice type
53 | var diags diag.Diagnostics
54 |
55 | node, err := client.GetNode(netid, mac)
56 | if err != nil {
57 | return diag.FromErr(err)
58 | }
59 | helper.SetIngressSchemaData(d, node, netid, mac)
60 |
61 | return diags
62 | }
63 |
64 | func resourceIngressUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
65 | // use the meta value to retrieve your client from the provider configure method
66 | resourceIngressDelete(ctx, d, meta)
67 |
68 | return resourceIngressCreate(ctx, d, meta)
69 | }
70 |
71 | func resourceIngressDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
72 | // use the meta value to retrieve your client from the provider configure method
73 | // client := meta.(*apiClient)
74 | client := meta.(*helper.Client)
75 | var diags diag.Diagnostics
76 |
77 | networkID := d.Get("netid").(string)
78 | mac := d.Get("mac").(string)
79 | _, err := client.DeleteIngress(networkID, mac)
80 | if err != nil {
81 | return diag.FromErr(err)
82 | }
83 |
84 | // d.SetId("") is automatically called assuming delete returns no errors, but
85 | // it is added here for explicitness.
86 | d.SetId("")
87 |
88 | return diags
89 | }
90 |
--------------------------------------------------------------------------------
/internal/provider/resource_ingress_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccResourceIngress(t *testing.T) {
11 | // t.Skip("resource not yet implemented, remove this once you add your own code")
12 | CreateTestData(t, true)
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccResourceIngress,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "netmaker_ingress.foo", "netid", regexp.MustCompile("^test")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccResourceIngress = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 |
35 | resource "netmaker_ingress" "foo" {
36 | netid = "test"
37 | mac = "01:02:03:04:05:06"
38 | }
39 | `
40 |
--------------------------------------------------------------------------------
/internal/provider/resource_network.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | "github.com/madacluster/netmaker-terraform-provider/helper"
10 | )
11 |
12 | func addLastupdated(s map[string]*schema.Schema) map[string]*schema.Schema {
13 | s["last_updated"] = &schema.Schema{
14 | Type: schema.TypeString,
15 | Computed: true,
16 | Optional: true,
17 | }
18 | return s
19 | }
20 |
21 | func resourceNetwork() *schema.Resource {
22 | return &schema.Resource{
23 | // This description is used by the documentation generator and the language server.
24 | Description: "Sample resource in the Terraform provider scaffolding.",
25 |
26 | CreateContext: resourceNetworkCreate,
27 | ReadContext: resourceNetworkRead,
28 | UpdateContext: resourceNetworkUpdate,
29 | DeleteContext: resourceNetworkDelete,
30 |
31 | Schema: addLastupdated(helper.CreateNetworkSchema()),
32 | }
33 | }
34 |
35 | func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
36 | // use the meta value to retrieve your client from the provider configure method
37 | var diags diag.Diagnostics
38 |
39 | client := meta.(*helper.Client)
40 | network, err := client.CreateNetworkFromSchema(d)
41 |
42 | if err != nil {
43 | return diag.Errorf("failed to create network: %s", err)
44 | }
45 | d.SetId(network.NetID)
46 | return diags
47 |
48 | }
49 |
50 | func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
51 | // use the meta value to retrieve your client from the provider configure method
52 | client := meta.(*helper.Client)
53 | networkID := d.Id()
54 |
55 | // Warning or errors can be collected in a slice type
56 | var diags diag.Diagnostics
57 |
58 | network, err := client.GetNetwork(networkID)
59 | if err != nil {
60 | return diag.FromErr(err)
61 | }
62 |
63 | if helper.SetNetworkSchemaData(d, network) != nil {
64 | return diag.FromErr(err)
65 | }
66 | return diags
67 | }
68 |
69 | func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
70 | // use the meta value to retrieve your client from the provider configure method
71 | client := meta.(*helper.Client)
72 | if d.HasChangesExcept("last_updated") {
73 | _, err := client.UpdateNetworkFromSchema(d)
74 | if err != nil {
75 | return diag.FromErr(err)
76 | }
77 | d.Set("last_updated", time.Now().Format(time.RFC850))
78 |
79 | }
80 |
81 | return resourceNetworkRead(ctx, d, meta)
82 | }
83 |
84 | func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
85 | // use the meta value to retrieve your client from the provider configure method
86 | // client := meta.(*apiClient)
87 | client := meta.(*helper.Client)
88 | var diags diag.Diagnostics
89 |
90 | networkID := d.Id()
91 | err := client.DeleteNetwork(networkID)
92 | if err != nil {
93 | return diag.FromErr(err)
94 | }
95 |
96 | // d.SetId("") is automatically called assuming delete returns no errors, but
97 | // it is added here for explicitness.
98 | d.SetId("")
99 |
100 | return diags
101 | }
102 |
--------------------------------------------------------------------------------
/internal/provider/resource_network_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccResourceNetwork(t *testing.T) {
11 | // t.Skip("resource not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccResourceNetwork,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "netmaker_network.foo", "netid", regexp.MustCompile("^netmakertes")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccResourceNetwork = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | resource "netmaker_network" "foo" {
35 | netid = "netmakertes"
36 | addressrange = "10.100.10.0/24"
37 | }
38 | `
39 |
--------------------------------------------------------------------------------
/internal/provider/resource_user.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | "github.com/madacluster/netmaker-terraform-provider/helper"
10 | )
11 |
12 | func resourceUser() *schema.Resource {
13 | return &schema.Resource{
14 | // This description is used by the documentation generator and the language server.
15 | Description: "Sample resource in the Terraform provider scaffolding.",
16 |
17 | CreateContext: resourceUserCreate,
18 | ReadContext: resourceUserRead,
19 | UpdateContext: resourceUserUpdate,
20 | DeleteContext: resourceUserDelete,
21 |
22 | Schema: addLastupdated(helper.CreateUserSchema()),
23 | }
24 | }
25 |
26 | func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
27 | // use the meta value to retrieve your client from the provider configure method
28 | // client := meta.(*apiClient)
29 | var diags diag.Diagnostics
30 |
31 | client := meta.(*helper.Client)
32 | user, err := client.CreateUserFromSchema(d)
33 |
34 | if err != nil {
35 | return diag.Errorf("failed to create models.User: %s", err)
36 | }
37 | d.SetId(user.UserName)
38 | return diags
39 |
40 | }
41 |
42 | func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
43 | // use the meta value to retrieve your client from the provider configure method
44 | client := meta.(*helper.Client)
45 | UserID := d.Id()
46 |
47 | // d.SetId(UserID)
48 |
49 | // Warning or errors can be collected in a slice type
50 | var diags diag.Diagnostics
51 |
52 | user, err := client.GetUser(UserID)
53 | if err != nil {
54 | return diag.FromErr(err)
55 | }
56 |
57 | if helper.SetUserSchemaData(d, user) != nil {
58 | return diag.FromErr(err)
59 | }
60 | return diags
61 | }
62 |
63 | func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
64 | // use the meta value to retrieve your client from the provider configure method
65 | client := meta.(*helper.Client)
66 | // UserID := d.Id()
67 | if d.HasChangesExcept("password") {
68 | _, err := client.UpdateUserFromSchema(d)
69 | if err != nil {
70 | return diag.FromErr(err)
71 | }
72 | d.Set("last_updated", time.Now().Format(time.RFC850))
73 |
74 | }
75 |
76 | return resourceUserRead(ctx, d, meta)
77 | }
78 |
79 | func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
80 | // use the meta value to retrieve your client from the provider configure method
81 | // client := meta.(*apiClient)
82 | client := meta.(*helper.Client)
83 | var diags diag.Diagnostics
84 |
85 | UserID := d.Id()
86 | err := client.DeleteUser(UserID)
87 | if err != nil {
88 | return diag.FromErr(err)
89 | }
90 |
91 | // d.SetId("") is automatically called assuming delete returns no errors, but
92 | // it is added here for explicitness.
93 | d.SetId("")
94 |
95 | return diags
96 | }
97 |
--------------------------------------------------------------------------------
/internal/provider/resource_user_test.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "regexp"
5 | "testing"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8 | )
9 |
10 | func TestAccResourceUser(t *testing.T) {
11 | // t.Skip("resource not yet implemented, remove this once you add your own code")
12 |
13 | resource.UnitTest(t, resource.TestCase{
14 | PreCheck: func() { testAccPreCheck(t) },
15 | ProviderFactories: providerFactories,
16 | Steps: []resource.TestStep{
17 | {
18 | Config: testAccResourceAdminUser,
19 | Check: resource.ComposeTestCheckFunc(
20 | resource.TestMatchResourceAttr(
21 | "netmaker_user.foo", "username", regexp.MustCompile("^netmakertes")),
22 | ),
23 | },
24 | },
25 | })
26 | }
27 |
28 | const testAccResourceAdminUser = `
29 | provider "netmaker" {
30 | username = "admin"
31 | password = "mx4S6JsSg7JWcZ"
32 | host = "http://localhost:8081"
33 | }
34 | resource "netmaker_network" "foo" {
35 | netid = "netmakertes"
36 | addressrange = "10.100.10.0/24"
37 | }
38 | resource "netmaker_user" "foo" {
39 | username = "netmakertes"
40 | password = "netmaker"
41 | networks = ["netmakertes"]
42 | }
43 | `
44 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "log"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
9 | "github.com/madacluster/netmaker-terraform-provider/internal/provider"
10 | )
11 |
12 | // Run "go generate" to format example terraform files and generate the docs for the registry/website
13 |
14 | // If you do not have terraform installed, you can remove the formatting command, but its suggested to
15 | // ensure the documentation is formatted properly.
16 | //go:generate terraform fmt -recursive ./examples/
17 |
18 | // Run the docs generation tool, check its repository for more information on how it works and how docs
19 | // can be customized.
20 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
21 |
22 | var (
23 | // these will be set by the goreleaser configuration
24 | // to appropriate values for the compiled binary
25 | version string = "dev"
26 |
27 | // goreleaser can also pass the specific commit if you want
28 | // commit string = ""
29 | )
30 |
31 | func main() {
32 | var debugMode bool
33 |
34 | flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve")
35 | flag.Parse()
36 |
37 | opts := &plugin.ServeOpts{ProviderFunc: provider.New(version)}
38 |
39 | if debugMode {
40 | // TODO: update this string with the full name of your provider as used in your configs
41 | err := plugin.Debug(context.Background(), "registry.terraform.io/hashicorp/netmaker", opts)
42 | if err != nil {
43 | log.Fatal(err.Error())
44 | }
45 | return
46 | }
47 |
48 | plugin.Serve(opts)
49 | }
50 |
--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 | // +build tools
3 |
4 | package tools
5 |
6 | // document generation
7 |
8 | import (
9 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
10 | )
11 |
--------------------------------------------------------------------------------