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

Enable test on github action workflow #2

Merged
merged 30 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
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: 2 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export GO111MODULE=on
export GOPRIVATE=github.com/streamnative
50 changes: 50 additions & 0 deletions .github/workflows/acctest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Tests
on: [ pull_request ]
jobs:
acctest:
name: Run acceptance tests
runs-on: ubuntu-latest
env:
ACC_TEST_SERVICE_ACCOUNT: ${{ secrets.ACC_TEST_SERVICE_ACCOUNT }}
GLOBAL_DEFAULT_API_SERVER: https://api.test.cloud.gcp.streamnative.dev
GLOBAL_DEFAULT_AUDIENCE: https://api.test.cloud.gcp.streamnative.dev
GLOBAL_DEFAULT_ISSUER: https://auth.test.cloud.gcp.streamnative.dev/
ACCESS_TOKEN: ${{ secrets.SNBOT_GITHUB_TOKEN }}
steps:
- name: Checkout Code
uses: actions/checkout@v3

- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.19
id: go

- name: Setup Git token
run: |
git config --global user.email "[email protected]"
git config --global user.name "StreamNative Bot"
git config --global url."https://streamnativebot:${ACCESS_TOKEN}@github.com/".insteadOf "https://github.com/"

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.5.7
terraform_wrapper: false

- name: Build the terraform-provider-streamnative
run: |
make build
mkdir -p $HOME/.terraform.d/plugins/linux_amd64
sudo mv terraform-provider-streamnative $HOME/.terraform.d/plugins/linux_amd64/
echo "The terraform-provider-streamnative location:" `readlink -f $HOME/.terraform.d/plugins/linux_amd64/`

- name: Run Acceptance Tests for the Provider
run: |
echo $ACC_TEST_SERVICE_ACCOUNT > $HOME/service_account.json
export KEY_FILE_PATH=$HOME/service_account.json
make testacc

- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
71 changes: 71 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### Go template
# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

### Terraform template
# Local .terraform directories
**/.terraform/*
.terraform.lock.hcl
# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log

# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
# .tfvars files are managed as part of configuration and so should be included in
# version control.
#
# example.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

### Example user template template
### Example user template

# IntelliJ project files
.idea
*.iml
out
gen

# generated binary
/terraform-provider-streamnative

# Mac hidden files
.DS_Store

temp/**

.vscode/
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
TEST?=./...
GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor)
HOSTNAME=registry.terraform.io
NAMESPACE?=streamnative
PKG_NAME=streamnative
BINARY=terraform-provider-${PKG_NAME}
VERSION?=0.1.0
OS := $(if $(GOOS),$(GOOS),$(shell go env GOOS))
ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH))
OS_ARCH := ${OS}_${ARCH}

default: build

build:
go build -o ${BINARY}

build-dev: build
mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${PKG_NAME}/${VERSION}/${OS_ARCH}
mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${PKG_NAME}/${VERSION}/${OS_ARCH}

testacc:
TF_ACC=1 go test $(TEST) -v -count 3 $(TESTARGS) -timeout 120m

.PHONY: build
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Terraform Provider for StreamNative cloud

### Why don't you use this framework https://github.com/hashicorp/terraform-plugin-framework?
This project relies on the cloud-cli project, cloud-cli doesn't work with go 1.20 yet, I tried to use the old version in the project but failed, we should consider migrating to this framework in the future.

### Why don't you use the latest version https://github.com/hashicorp/terraform-plugin-sdk/tree/v2.31.0?
This project relies on the cloud-cli project, cloud-cli doesn't work with go 1.20 yet.

# Prerequisites

- [Terraform](https://www.terraform.io/downloads.html) 1.15.7 or later
- [Go](https://golang.org/doc/install) 1.19

# Installation

- From terraform registry(TODO)

```hcl
terraform {
required_providers {
pulsar = {
version = "0.1.0"
source = "registry.terraform.io/streamnative/streamnative"
}
}
}
```

- From source code

- Clone this repository and cd into the directory
- Run `make build`, it will generate a binary file named `terraform-provider-streamnative`
- Copy this `terraform-provider-streamnative` binary file to your terraform plugin directory based on your OS:

| Operating System | User plugins directory |
| ---------------- | --------------------------------------------------------------------------------------------- |
| Windows(amd64) | %APPDATA%\terraform.d\plugins\registry.terraform.io\streamnative\streamnative\0.1.0\windows_amd64\ |
| Linux(amd64) | ~/.terraform.d/plugins/registry.terraform.io/streamnative/streamnative/0.1.0/linux_amd64/ |
| MacOS(amd64) | ~/.terraform.d/plugins/registry.terraform.io/streamnative/streamnative/0.1.0/darwin_amd64/ |

- Run `make build-dev`, it will build the binary and copy it to the plugin directory automatically.
23 changes: 23 additions & 0 deletions cloud/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cloud

import (
"fmt"
cloudclient "github.com/streamnative/cloud-api-server/pkg/client/clientset_generated/clientset"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

func getFactoryFromMeta(meta interface{}) cmdutil.Factory {
return meta.(cmdutil.Factory)
}

func getClientSet(factory cmdutil.Factory) (*cloudclient.Clientset, error) {
config, err := factory.ToRESTConfig()
if err != nil {
return nil, fmt.Errorf("ToRESTConfig: %v", err)
}
clientSet, err := cloudclient.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("NewForConfig: %v", err)
}
return clientSet, nil
}
155 changes: 155 additions & 0 deletions cloud/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package cloud

import (
"context"
"encoding/base64"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mitchellh/go-homedir"
"github.com/streamnative/cloud-cli/pkg/auth"
"github.com/streamnative/cloud-cli/pkg/auth/store"
"github.com/streamnative/cloud-cli/pkg/cmd"
"github.com/streamnative/cloud-cli/pkg/config"
"github.com/streamnative/cloud-cli/pkg/plugin"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"os"
"path/filepath"
)

const (
GlobalDefaultIssuer = "https://auth.streamnative.cloud/"
GlobalDefaultAudience = "https://api.streamnative.cloud"
GlobalDefaultAPIServer = "https://api.streamnative.cloud"
GlobalDefaultCertificateAuthorityData = ``
)

var descriptions map[string]string

func init() {
descriptions = map[string]string{
"key_file_path": "The path of the private key file",
"organization": "The organization name",
"name": "The service account name",
"admin": "Whether the service account is admin",
"private_key_data": "The private key data",
}
}

func Provider() *schema.Provider {
provider := &schema.Provider{
Schema: map[string]*schema.Schema{
"key_file_path": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("KEY_FILE_PATH", nil),
Description: descriptions["key_file_path"],
},
},
ResourcesMap: map[string]*schema.Resource{
"streamnative_service_account": resourceServiceAccount(),
},
}
provider.ConfigureContextFunc = func(_ context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
return providerConfigure(d, provider.TerraformVersion)
}
return provider
}

func providerConfigure(d *schema.ResourceData, terraformVersion string) (interface{}, diag.Diagnostics) {
_ = terraformVersion

keyFilePath := d.Get("key_file_path").(string)

home, err := homedir.Dir()
if err != nil {
return nil, diag.FromErr(err)
}
configDir := filepath.Join(home, ".streamnative")
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err = os.MkdirAll(configDir, 0755); err != nil {
return nil, diag.FromErr(err)
}
}
defaultIssuer := os.Getenv("GLOBAL_DEFAULT_ISSUER")
if defaultIssuer == "" {
defaultIssuer = GlobalDefaultIssuer
}
defaultAudience := os.Getenv("GLOBAL_DEFAULT_AUDIENCE")
if defaultAudience == "" {
defaultAudience = GlobalDefaultAudience
}
defaultApiServer := os.Getenv("GLOBAL_DEFAULT_API_SERVER")
if defaultApiServer == "" {
defaultApiServer = GlobalDefaultAPIServer
}
credsProvider := auth.NewClientCredentialsProviderFromKeyFile(keyFilePath)
keyFile, err := credsProvider.GetClientCredentials()
if err != nil {
return nil, diag.FromErr(err)
}
issuer := auth.Issuer{
IssuerEndpoint: defaultIssuer,
ClientID: keyFile.ClientID,
Audience: defaultAudience,
}
flow, err := auth.NewDefaultClientCredentialsFlow(issuer, keyFilePath)
if err != nil {
return nil, diag.FromErr(err)
}
grant, err := flow.Authorize()
if err != nil {
return nil, diag.FromErr(err)
}
streams := genericclioptions.IOStreams{
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
}
options := cmd.NewOptions(streams)
options.ConfigDir = configDir
options.ConfigPath = filepath.Join(configDir, "config")
options.BackendOverride = "memory"
snConfig := &config.SnConfig{
Server: defaultApiServer,
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(GlobalDefaultCertificateAuthorityData)),
Auth: config.Auth{
IssuerEndpoint: defaultIssuer,
Audience: defaultAudience,
ClientID: keyFile.ClientID,
},
}
err = options.SaveConfig(snConfig)
if err != nil {
return nil, diag.FromErr(err)
}
apc := &clientcmdapi.AuthProviderConfig{
Name: "streamnative",
}
// Pre-check if the auth provider is already exist for avoid issue
// auth Provider Plugin streamnative was registered twice
provider, _ := rest.GetAuthProvider("", apc, nil)
if provider == nil {
err = options.Complete()
if err != nil {
return nil, diag.FromErr(err)
}
} else {
options.Store = store.NewMemoryStore()
options.Factory, err = plugin.NewDefaultFactory(options.Store, func() (auth.Issuer, error) {
return issuer, nil
})
err = options.ServerOptions.Complete(options)
if err != nil {
return nil, diag.FromErr(err)
}
}
err = options.Store.SaveGrant(issuer.Audience, *grant)
if err != nil {
return nil, diag.FromErr(err)
}
factory := cmdutil.NewFactory(options)
return factory, nil
}
Loading