Skip to content

Commit

Permalink
Watch v2 (#9)
Browse files Browse the repository at this point in the history
- Xray API v2 support for watches and policies
- Use of singular key names instead of plural
- Use TypeSet instead of TypeList to prevent sorting problems in TF state with multiple elements
- HCL changes
- #1, #2, #4
  • Loading branch information
danielmkn authored Dec 14, 2021
1 parent b460e6d commit 5f5b56f
Show file tree
Hide file tree
Showing 35 changed files with 2,997 additions and 894 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: chb0github
assignees: danielmkn

---

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This GitHub action can publish assets for release when a tag is created.
# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
#
# This uses an action (paultyng/ghaction-import-gpg) that assumes you set your
# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your
# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE`
# secret. If you would rather own your own GPG handling, please fork this action
# or use an alternative one for key handling.
Expand Down Expand Up @@ -33,7 +33,7 @@ jobs:
-
name: Import GPG key
id: import_gpg
uses: paultyng/[email protected]
uses: hashicorp/[email protected]
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
PASSPHRASE: ${{ secrets.PASSPHRASE }}
Expand Down
31 changes: 17 additions & 14 deletions GNUmakefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
TEST?=./...
PKG_NAME=pkg/xray
PKG_VERSION_PATH=github.com/jfrog/terraform-provider-xray/${PKG_NAME}
#VERSION := $(shell git tag --sort=-creatordate | head -1 | sed -n 's/v\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1.\2.\3/p')
# Replace explicit version after the first release
VERSION := 0.0.0
NEXT_VERSION := $(shell echo ${VERSION}| awk -F '.' '{print $$1 "." $$2 "." $$3 +1 }' )
BINARY_NAME=terraform-provider-xray
BUILD_PATH=terraform.d/plugins/registry.terraform.io/jfrog/xray/${NEXT_VERSION}/darwin_amd64

default: build

install:
mkdir -p terraform.d/plugins/registry.terraform.io/jfrog/xray/${NEXT_VERSION}/darwin_amd64 && \
(test -f terraform-provider-xray || go build -ldflags="-X 'xray.Version=${NEXT_VERSION}'") && \
mv terraform-provider-xray terraform.d/plugins/registry.terraform.io/jfrog/xray/${NEXT_VERSION}/darwin_amd64 && \
mkdir -p ${BUILD_PATH} && \
(test -f ${BINARY_NAME} || go build -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_VERSION}'") && \
mv ${BINARY_NAME} ${BUILD_PATH} && \
terraform init

clean:
rm -fR .terraform.d/ .terraform terraform.tfstate* terraform.d/
rm -fR .terraform.d/ .terraform terraform.tfstate* terraform.d/ .terraform.lock.hcl

release:
@git tag ${NEXT_VERSION} && git push --mirror
@echo "Pushed ${NEXT_VERSION}"
@git tag v${NEXT_VERSION} && git push --mirror
@echo "Pushed v${NEXT_VERSION}"

build: fmtcheck
go build -ldflags="-X 'xray.Version=${NEXT_VERSION}'"

debug_install:
mkdir -p terraform.d/plugins/registry.terraform.io/jfrog/xray/${NEXT_VERSION}/darwin_amd64 && \
(test -f terraform-provider-xray || go build -gcflags "all=-N -l" -ldflags="-X 'xray.Version=${NEXT_VERSION}-develop'") && \
mv terraform-provider-xray terraform.d/plugins/registry.terraform.io/jfrog/xray/${NEXT_VERSION}/darwin_amd64 && \
mkdir -p ${BUILD_PATH} && \
(test -f ${BINARY_NAME} || go build -gcflags "all=-N -l" -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_VERSION}-develop'") && \
mv ${BINARY_NAME} ${BUILD_PATH} && \
terraform init


Expand All @@ -38,18 +41,18 @@ attach:
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient attach $$(pgrep terraform-provider-xray)

acceptance: fmtcheck
export TF_ACC=1
test -n ARTIFACTORY_USERNAME && test -n ARTIFACTORY_URL && test -n ARTIFACTORY_ACCESS_TOKEN \
&& go test -v -parallel 20 ./pkg/...

export TF_ACC=1 && go test -v -parallel 20 ./pkg/...

fmt:
@echo "==> Fixing source code with gofmt..."
@gofmt -s -w ./$(PKG_NAME)
(command -v goimports &> /dev/null || go get golang.org/x/tools/cmd/goimports) && goimports -w pkg/xray
(command -v goimports &> /dev/null || go get golang.org/x/tools/cmd/goimports) && goimports -w ${PKG_NAME}

fmtcheck:
@echo "==> Checking that code complies with gofmt requirements..."
@sh -c "find . -name '*.go' -not -name '*vendor*' -print0 | xargs -0 gofmt -l -s"

doc:
go generate

.PHONY: build fmt
44 changes: 22 additions & 22 deletions docs/debug.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
# Debugging a TerraForm provider
# Debugging a TerraForm provider

## Understanding the design

In order to do it, you first have to understand how Go builds apps, and then how terraform works with it.

Every terraform provider is a sort of `module`. In order to support an open, modular system, in almost any language, you need to be able to dynamically load modules and interact with them. Terraform is no exception.

However, the go lang team long ago decided to compile to statically linked applications;
any dependencies you have will be compiled into 1 single binary. Unlike in other native languages (like C, or C++), a
However, the go lang team long ago decided to compile to statically linked applications;
any dependencies you have will be compiled into 1 single binary. Unlike in other native languages (like C, or C++), a
`.dll` or `.so` is not used; there is no dynamic library to load at runtime and thus, modularity becomes a whole other trick.
This is done to avoid the notorious **dll hell** that was so common up until most modern systems included some
This is done to avoid the notorious **dll hell** that was so common up until most modern systems included some
kind of dependency management. And yes, it can still be an issue.

Every terraform provider is its own mini RPC server. When terraform runs your provider, it actually starts a new process that is your provider, and connects to it through
this RPC channel. Compounding the problem is that the lifetime of your provider process is very much
Every terraform provider is its own mini RPC server. When terraform runs your provider, it actually starts a new process that is your provider, and connects to it through
this RPC channel. Compounding the problem is that the lifetime of your provider process is very much
ephemeral; potentially lasting no more and a few seconds. It's this process you need to connect to with your debugger

### Normal debugging
Normally, you would directly spin-up your app, and it would load modules into application memory. That's why you can actually
debug it, because your debugger knows how to find the exact memory address for your provider. However, you don't have
this arrangement, and you need to do a _remote_ debug session.
debug it, because your debugger knows how to find the exact memory address for your provider. However, you don't have
this arrangement, and you need to do a _remote_ debug session.

### The conundrum
So, you don't load terraform directly, and even if you did, your `module` (a.k.a your provider) is in the memory
space of an entirely different process; and that lasts no more than a few seconds, potentially.
So, you don't load terraform directly, and even if you did, your `module` (a.k.a your provider) is in the memory
space of an entirely different process; and that lasts no more than a few seconds, potentially.

## The solution

1. You need the debugging tool [delve](https://github.com/go-delve/delve).
2. You are going to have to place a little bit of shim code close to the spot in the code where you want to begin
debugging. We need to stop this provider process from exiting before we can connect. So, put this bit of code in place:
debugging. We need to stop this provider process from exiting before we can connect. So, put this bit of code in place:
```go
connected := false
for !connected {
time.Sleep(time.Second) // set breakpoint here
}
for !connected {
time.Sleep(time.Second) // set breakpoint here
}
```
This code effectively creates an infinite sleep loop; but that's actually essential to solving the problem.

3. Place a break point right inside this loop. It won't do anything, yet.
3. Place a break point right inside this loop. It won't do anything, yet.
4. Now run the terraform commands you need to, to engage the code you're desiring to debug. Upon doing so,
terraform will basically stop, as it waits on a response from you provider; because you put an infinite sleep loop in
terraform will basically stop, as it waits on a response from you provider; because you put an infinite sleep loop in
5. You must now tell `delve` to connect to this remote process using it's PID. This isn't as hard as it seems.
Run this commands:
`dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient attach $(pgrep terraform-provider-xray)`
The last argument gets the `PID` for your provider and supplies it to `delve` to connect. Immediately upon running this
command, you're going to hit your break point. Please make sure to substitute `terraform-provider-xray` for your provider name
6. To exit this infinite loop, use your debugger to set `connected` to `true`. By doing so you change the loop predicate
and it will exit this loop on the next iteration.
Run this commands:
`dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient attach $(pgrep terraform-provider-xray)`
The last argument gets the `PID` for your provider and supplies it to `delve` to connect. Immediately upon running this
command, you're going to hit your break point. Please make sure to substitute `terraform-provider-xray` for your provider name
6. To exit this infinite loop, use your debugger to set `connected` to `true`. By doing so you change the loop predicate
and it will exit this loop on the next iteration.
7. *DEBUG!* - At this point you can, step, watch, drop the call stack, etc. Your whole arsenel is available
180 changes: 180 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1 +1,181 @@
---
layout: ""
page_title: "JFrog Xray Provider"
description: |-
The Xray provider is used to interact with the resources supported by JFrog Xray.
---

# JFrog Xray Provider

The [Xray](https://jfrog.com/xray/) provider is used to interact with the
resources supported by JFrog Xray. Xray is a part of JFrog Artifactory and can't be used separately.
The provider needs to be configured with the proper credentials before it can be used.
Xray API documentation can be found [here](https://www.jfrog.com/confluence/display/JFROG/Xray+REST+API)

Links to documentation for specific resources can be found in the table of contents to the left.

This provider requires access to Artifactory and Xray APIs, which are only available in the _licensed_ pro and enterprise editions.
You can determine which license you have by accessing the following URL
`${host}/artifactory/api/system/licenses/`

You can either access it via api, or web browser - it does require admin level credentials, but it's one of the few APIs that will work without a license (side node: you can also install your license here with a `POST`)

```bash
curl -sL ${host}/projects/api/system/licenses/ | jq .
{
"type" : "Enterprise Plus Trial",
"validThrough" : "Jan 29, 2022",
"licensedTo" : "JFrog Ltd"
}
```

The following 3 license types (`jq .type`) do **NOT** support APIs:
- Community Edition for C/C++
- JCR Edition
- OSS

## Example Usage

```terraform
# Required for Terraform 0.13 and up (https://www.terraform.io/upgrade-guides/0-13.html)
terraform {
required_providers {
xray = {
source = "registry.terraform.io/jfrog/xray"
version = "0.0.1"
}
}
}
provider "xray" {
url = "artifactory.site.com/xray"
access_token = "abc..xy"
// Also user can supply the following env vars:
// ARTIFACTORY_URL or JFROG_URL
// XRAY_ACCESS_TOKEN or JFROG_ACCESS_TOKEN
}
resource "random_id" "randid" {
byte_length = 2
}
resource "xray_security_policy" "security_policy" {
name = "test-security-policy-severity-${random_id.randid.dec}"
description = "Security policy description"
type = "security"
rule {
name = "rule-name-severity"
priority = 1
criteria {
min_severity = "High"
}
actions {
webhooks = []
mails = ["[email protected]"]
block_release_bundle_distribution = true
fail_build = true
notify_watch_recipients = true
notify_deployer = true
create_ticket_enabled = false // set to true only if Jira integration is enabled
build_failure_grace_period_in_days = 5 // use only if fail_build is enabled
block_download {
unscanned = true
active = true
}
}
}
}
resource "xray_license_policy" "license_policy" {
name = "test-license-policy-allowed-${random_id.randid.dec}"
description = "License policy, allow certain licenses"
type = "license"
rule {
name = "License_rule"
priority = 1
criteria {
allowed_licenses = ["Apache-1.0", "Apache-2.0"]
allow_unknown = false
multi_license_permissive = true
}
actions {
webhooks = []
mails = ["[email protected]"]
block_release_bundle_distribution = false
fail_build = true
notify_watch_recipients = true
notify_deployer = true
create_ticket_enabled = false // set to true only if Jira integration is enabled
custom_severity = "High"
build_failure_grace_period_in_days = 5 // use only if fail_build is enabled
block_download {
unscanned = true
active = true
}
}
}
}
resource "xray_watch" "all-repos" {
name = "all-repos-watch-${random_id.randid.dec}"
description = "Watch for all repositories, matching the filter"
active = true
watch_resource {
type = "all-repos"
filter {
type = "regex"
value = ".*"
}
}
assigned_policy {
name = xray_security_policy.security_policy.name
type = "security"
}
assigned_policy {
name = xray_license_policy.license_policy.name
type = "license"
}
watch_recipients = ["[email protected]", "[email protected]"]
}
```

## Authentication

The Xray provider supports one type of authentication using Bearer token.

### Bearer Token
Artifactory access tokens may be used via the Authorization header by providing the `access_token` field to the provider
block. Getting this value from the environment is supported with the `XRAY_ACCESS_TOKEN`,
or `JFROG_ACCESS_TOKEN` variables.
Set `url` field to provide JFrog Xray URL. Alternatively you can set `ARTIFACTORY_URL`, `JFROG_URL` or `PROJECTS_URL` variables.

Usage:
```hcl
# Configure the Xray provider
provider "xray" {
url = "artifactory.site.com/xray"
access_token = "abc...xy"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- **access_token** (String, Sensitive) This is a bearer token that can be given to you by your admin under `Identity and Access`
- **url** (String) URL of Artifactory. This can also be sourced from the `XRAY_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set.
Empty file.
Loading

0 comments on commit 5f5b56f

Please sign in to comment.