Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add license resource #20

Merged
merged 5 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading