Skip to content

Commit

Permalink
Merge pull request #20 from jfrog/GH-12-add-license-resource
Browse files Browse the repository at this point in the history
Add license resource
  • Loading branch information
alexhung authored Jan 4, 2024
2 parents a4121f3 + 9e3a3ab commit d32f09e
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .jfrog-pipelines/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ pipelines:
export DESCRIPTION="All tests passed successfully."
git clone https://${int_partnership_github_token}@github.com/jfrog/terraform-provider-shared.git
./terraform-provider-shared/scripts/github-status.sh ${res_terraform_provider_platform_gitProvider_token} ${res_terraform_provider_platform_gitRepoFullName} ${res_terraform_provider_platform_commitSha}
send_notification partnership_slack --text "<${res_terraform_provider_platform_gitRepoRepositoryHttpsUrl}|Terraform Provider>. A new PR was submitted by *${PR_COMMITTER}* - <${PR_URL}|${PR_TITLE}>, branch *${PR_BRANCH}*. Changes tested successfully. <@U01H1SLSPA8> or <@UNDRUL1EU> please, review and merge."
send_notification partnership_slack --text "<${res_terraform_provider_platform_gitRepoRepositoryHttpsUrl}|Terraform Provider Platform>. A new PR was submitted by *${PR_COMMITTER}* - <${PR_URL}|${PR_TITLE}>, branch *${PR_BRANCH}*. Changes tested successfully. <@U01H1SLSPA8> or <@UNDRUL1EU> please, review and merge."
fi
onFailure:
- echo "Failure, sending status to GitHub and Slack."
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 1.2.0 (Jan 4, 2023). Tested on Artifactory 7.71.11 with Terraform CLI v1.6.6

FEATURES:

* **New Resource:** `platform_license`: PR: [#20](https://github.com/jfrog/terraform-provider-platform/pull/20) Issue: [#12](https://github.com/jfrog/terraform-provider-platform/issues/12)

## 1.1.0 (Dec 14, 2023). Tested on Artifactory 7.71.10 with Terraform CLI v1.6.6

FEATURES:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ See the [contribution guide](CONTRIBUTIONS.md).

## License

Copyright (c) 2023 JFrog.
Copyright (c) 2024 JFrog.

Apache 2.0 licensed, see [LICENSE][LICENSE] file.

Expand Down
45 changes: 45 additions & 0 deletions docs/resources/license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "platform_license Resource - terraform-provider-platform"
subcategory: ""
description: |-
Provides a JFrog license https://jfrog.com/help/r/jfrog-platform-administration-documentation/managing-licenses resource to install/update license.
~>Only available for self-hosted instances.
---

# platform_license (Resource)

Provides a JFrog [license](https://jfrog.com/help/r/jfrog-platform-administration-documentation/managing-licenses) resource to install/update license.

~>Only available for self-hosted instances.

## Example Usage

```terraform
resource "platform_license" "my-ent-license" {
name = "my-enterprise-license"
key = <<EOT
bGljZ
...
09Cg==
EOT
}
```

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

### Required

- `key` (String) License key. Any newline characters must be represented by escape sequence `
`

### Optional

- `name` (String) Name of the license

### Read-Only

- `licensed_to` (String) Customer name the license belongs to.
- `type` (String) Type of the license.
- `valid_through` (String) Date of the license is valid through.
8 changes: 8 additions & 0 deletions examples/resources/platform_license/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "platform_license" "my-ent-license" {
name = "my-enterprise-license"
key = <<EOT
bGljZ
...
09Cg==
EOT
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-go v0.20.0
github.com/hashicorp/terraform-plugin-testing v1.6.0
github.com/jfrog/terraform-provider-shared v1.21.1
github.com/jfrog/terraform-provider-shared v1.21.3
github.com/samber/lo v1.39.0
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jfrog/terraform-provider-shared v1.21.1 h1:hBlk7087fjMVk7fdx2BkpZM3RnLIzS6fGNZEJc5HLYc=
github.com/jfrog/terraform-provider-shared v1.21.1/go.mod h1:Ioq1OBLb2h9uj4t4KzBEfA1piHbUg+Lgvn84Vbzk5uI=
github.com/jfrog/terraform-provider-shared v1.21.3 h1:zAE7mWTZ0WUjr7d7dC0WOPNU9xpwuc7mh2RB+T7XNP8=
github.com/jfrog/terraform-provider-shared v1.21.3/go.mod h1:xwhh2ugF0XwEd8wlalqC54ibJBmSlmLLhA5l04gBJsU=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
5 changes: 3 additions & 2 deletions pkg/platform/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
validator_string "github.com/jfrog/terraform-provider-shared/validator/fw/string"
)

var Version = "0.0.1"
var Version = "1.0.0"

// needs to be exported so make file can update this
var productId = "terraform-provider-platform/" + Version
Expand Down Expand Up @@ -141,8 +141,9 @@ func (p *PlatformProvider) DataSources(ctx context.Context) []func() datasource.

func (p *PlatformProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewWorkerServiceResource,
NewLicenseResource,
NewReverseProxyResource,
NewWorkerServiceResource,
}
}

Expand Down
249 changes: 249 additions & 0 deletions pkg/platform/resource_license.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
package platform

import (
"context"
"net/http"
"strings"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/jfrog/terraform-provider-shared/util"
utilfw "github.com/jfrog/terraform-provider-shared/util/fw"
"github.com/samber/lo"
)

const (
licensePostEndpoint = "/artifactory/api/system/licenses"
licenseGetEndpoint = "/artifactory/api/system/license"
)

var _ resource.Resource = (*licenseResource)(nil)

type licenseResource struct {
ProviderData util.ProvderMetadata
}

func NewLicenseResource() resource.Resource {
return &licenseResource{}
}

func (r *licenseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_license"
}

func (r *licenseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"key": schema.StringAttribute{
Required: true,
Description: "License key. Any newline characters must be represented by escape sequence `\n`",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"name": schema.StringAttribute{
Optional: true,
Description: "Name of the license",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"type": schema.StringAttribute{
Computed: true,
Description: "Type of the license.",
},
"valid_through": schema.StringAttribute{
Computed: true,
Description: "Date of the license is valid through.",
},
"licensed_to": schema.StringAttribute{
Computed: true,
Description: "Customer name the license belongs to.",
},
},
MarkdownDescription: "Provides a JFrog [license](https://jfrog.com/help/r/jfrog-platform-administration-documentation/managing-licenses) resource to install/update license.\n\n~>Only available for self-hosted instances.",
}
}

type licenseResourceModel struct {
Key types.String `tfsdk:"key"`
Name types.String `tfsdk:"name"`
Type types.String `tfsdk:"type"`
ValidThrough types.String `tfsdk:"valid_through"`
LicensedTo types.String `tfsdk:"licensed_to"`
}

func (r *licenseResourceModel) fromAPIModel(ctx context.Context, apiModel *licenseAPIGetModel) (ds diag.Diagnostics) {
r.Type = types.StringValue(apiModel.Type)
r.ValidThrough = types.StringValue(apiModel.ValidThrough)
r.LicensedTo = types.StringValue(apiModel.LicensedTo)

return
}

type licenseAPIPostRequestModel struct {
Key string `json:"licenseKey"`
}

type licenseAPIPostResonseModel struct {
Status int `json:"status"`
Messages map[string]string `json:"messages"`
}

type licenseAPIGetModel struct {
Type string `json:"type"`
ValidThrough string `json:"validThrough"`
LicensedTo string `json:"licensedTo"`
}

func (r *licenseResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
r.ProviderData = req.ProviderData.(util.ProvderMetadata)
}

func (r *licenseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan licenseResourceModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

license := licenseAPIPostRequestModel{
Key: plan.Key.ValueString(),
}

var errorResult licenseAPIPostResonseModel

response, err := r.ProviderData.Client.R().
SetBody(&license).
SetError(&errorResult).
Post(licensePostEndpoint)

if err != nil {
if !(response.StatusCode() == http.StatusBadRequest &&
errorResult.Messages[license.Key] == "License already exists.") {
messages := lo.Values[string, string](errorResult.Messages)
utilfw.UnableToCreateResourceError(resp, strings.Join(messages, ","))
return
}
}

var licenseGet licenseAPIGetModel

response, err = r.ProviderData.Client.R().
SetResult(&licenseGet).
Get(licenseGetEndpoint)

// Treat HTTP 404 Not Found status as a signal to recreate resource
// and return early
if err != nil {
utilfw.UnableToCreateResourceError(resp, response.String())
return
}

// Convert from the API data model to the Terraform data model
// and refresh any attribute values.
resp.Diagnostics.Append(plan.fromAPIModel(ctx, &licenseGet)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *licenseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state licenseResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

var license licenseAPIGetModel

response, err := r.ProviderData.Client.R().
SetResult(&license).
Get(licenseGetEndpoint)

// Treat HTTP 404 Not Found status as a signal to recreate resource
// and return early
if err != nil {
utilfw.UnableToRefreshResourceError(resp, response.String())
return
}

// Convert from the API data model to the Terraform data model
// and refresh any attribute values.
resp.Diagnostics.Append(state.fromAPIModel(ctx, &license)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *licenseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan licenseResourceModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

license := licenseAPIPostRequestModel{
Key: plan.Key.ValueString(),
}

var errorResult licenseAPIPostResonseModel

response, err := r.ProviderData.Client.R().
SetBody(&license).
SetError(&errorResult).
Post(licensePostEndpoint)

if err != nil {
if !(response.StatusCode() == http.StatusBadRequest &&
errorResult.Messages[license.Key] == "License already exists.") {
messages := lo.Values[string, string](errorResult.Messages)
utilfw.UnableToUpdateResourceError(resp, strings.Join(messages, ","))
return
}
}

var licenseGet licenseAPIGetModel

response, err = r.ProviderData.Client.R().
SetResult(&licenseGet).
Get(licenseGetEndpoint)

// Treat HTTP 404 Not Found status as a signal to recreate resource
// and return early
if err != nil {
utilfw.UnableToUpdateResourceError(resp, response.String())
return
}

// Convert from the API data model to the Terraform data model
// and refresh any attribute values.
resp.Diagnostics.Append(plan.fromAPIModel(ctx, &licenseGet)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *licenseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
resp.Diagnostics.AddWarning(
"Unable to Delete Resource",
"License cannot be deleted.",
)
}
Loading

0 comments on commit d32f09e

Please sign in to comment.