Skip to content

Commit

Permalink
feat: add network, mysql and postgres modules
Browse files Browse the repository at this point in the history
  • Loading branch information
liu-hm19 committed Mar 26, 2024
1 parent 632a9e0 commit af9a793
Show file tree
Hide file tree
Showing 46 changed files with 5,493 additions and 7 deletions.
11 changes: 11 additions & 0 deletions modules/mysql/example/dev/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "example"

[dependencies]
kam = { git = "https://github.com/KusionStack/kam.git", tag = "0.1.0" }
network = { oci = "oci://ghcr.io/kusionstack/network", tag = "0.1.0" }
mysql = { oci = "oci://ghcr.io/kusionstack/mysql", tag = "0.1.0" }

[profile]
entries = ["main.k"]

34 changes: 34 additions & 0 deletions modules/mysql/example/dev/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The configuration codes in perspective of developers.
import kam.v1.app_configuration as ac
import kam.v1.workload as wl
import kam.v1.workload.container as c
import network as n
import mysql.mysql

quickstart: ac.AppConfiguration {
workload: wl.Service {
containers: {
quickstart: c.Container {
image: "kusionstack/kusion-quickstart:latest"
env: {
"DB_HOST": "$(KUSION_DB_HOST_TEST_DATABASE)"
"DB_USERNAME": "$(KUSION_DB_USERNAME_TEST_DATABASE)"
"DB_PASSWORD": "$(KUSION_DB_PASSWORD_TEST_DATABASE)"
}
}
}
}
accessories: {
"network": n.Network {
ports: [
n.Port {
port: 8080
}
]
}
"mysql": mysql.MySQL {
type: "local"
version: "8.0"
}
}
}
1 change: 1 addition & 0 deletions modules/mysql/example/dev/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name: dev
1 change: 1 addition & 0 deletions modules/mysql/example/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name: example
3 changes: 3 additions & 0 deletions modules/mysql/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[package]
name = "mysql"
version = "0.1.0"
8 changes: 5 additions & 3 deletions modules/mysql/mysql.k
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ schema MySQL:

import catalog.models.schema.v1.accessories.mysql

mysql: mysql.MySQL {
type: "local"
version: "5.7"
accessories: {
"mysql": mysql.MySQL {
type: "local"
version: "8.0"
}
}
"""

Expand Down
36 changes: 36 additions & 0 deletions modules/mysql/src/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
TEST?=$$(go list ./... | grep -v 'vendor')
###### chang variables below according to your own modules ###
NAMESPACE=kusionstack
NAME=mysql
VERSION=0.1.0
BINARY=../bin/kusion-module-${NAME}_${VERSION}

LOCAL_ARCH := $(shell uname -m)
ifeq ($(LOCAL_ARCH),x86_64)
GOARCH_LOCAL := amd64
else
GOARCH_LOCAL := $(LOCAL_ARCH)
endif
export GOOS_LOCAL := $(shell uname|tr 'A-Z' 'a-z')
export OS_ARCH ?= $(GOARCH_LOCAL)

default: install

build-darwin:
GOOS=darwin GOARCH=arm64 go build -o ${BINARY} ./${NAME}

install: build-darwin
# copy module binary to $KUSION_HOME. e.g. ~/.kusion/modules/kusionstack/mysql/v0.1.0/darwin/arm64/kusion-module-mysql_0.1.0
mkdir -p ${KUSION_HOME}/modules/${NAMESPACE}/${NAME}/${VERSION}/${GOOS_LOCAL}/${OS_ARCH}
cp ${BINARY} ${KUSION_HOME}/modules/${NAMESPACE}/${NAME}/${VERSION}/${GOOS_LOCAL}/${OS_ARCH}

release:
GOOS=darwin GOARCH=arm64 go build -o ${BINARY}_darwin_arm64 ./${NAME}
GOOS=darwin GOARCH=amd64 go build -o ${BINARY}_darwin_amd64 ./${NAME}
GOOS=linux GOARCH=arm64 go build -o ${BINARY}_linux_arm64 ./${NAME}
GOOS=linux GOARCH=amd64 go build -o ${BINARY}_linux_amd64 ./${NAME}
GOOS=windows GOARCH=amd64 go build -o ${BINARY}_windows_amd64 ./${NAME}
GOOS=windows GOARCH=386 go build -o ${BINARY}_windows_386 ./${NAME}

test:
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 5m
217 changes: 217 additions & 0 deletions modules/mysql/src/alicloud_rds.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package main

import (
"errors"
"os"
"strings"

"kusionstack.io/kusion-module-framework/pkg/module"
apiv1 "kusionstack.io/kusion/pkg/apis/core/v1"
"kusionstack.io/kusion/pkg/modules"
)

var ErrEmptyAlicloudProviderRegion = errors.New("empty alicloud provider region")

var (
alicloudRegionEnv = "ALICLOUD_REGION"
alicloudDBInstance = "alicloud_db_instance"
alicloudDBConnection = "alicloud_db_connection"
alicloudRDSAccount = "alicloud_rds_account"
)

var defaultAlicloudProviderCfg = apiv1.ProviderConfig{
Source: "aliyun/alicloud",
Version: "1.209.1",
}

type alicloudServerlessConfig struct {
AutoPause bool `yaml:"auto_pause" json:"auto_pause"`
SwitchForce bool `yaml:"switch_force" json:"switch_force"`
MaxCapacity int `yaml:"max_capacity,omitempty" json:"max_capacity,omitempty"`
MinCapacity int `yaml:"min_capacity,omitempty" json:"min_capacity,omitempty"`
}

// GenerateAlicloudResources generates Alicloud provided MySQL database instance.
func (mysql *MySQL) GenerateAlicloudResources(request *module.GeneratorRequest) ([]apiv1.Resource, []apiv1.Patcher, error) {
var resources []apiv1.Resource
var patchers []apiv1.Patcher

// Set the Alicloud provider with the default provider config.
alicloudProviderCfg := defaultAlicloudProviderCfg

// Get the Alicloud Terraform provider region, which should not be empty.
var region string
if region = module.TerraformProviderRegion(alicloudProviderCfg); region == "" {
region = os.Getenv(alicloudRegionEnv)
}
if region == "" {
return nil, nil, ErrEmptyAlicloudProviderRegion
}

// Build random_password resource.
randomPasswordRes, randomPasswordID, err := mysql.GenerateTFRandomPassword(request)
if err != nil {
return nil, nil, err
}
resources = append(resources, *randomPasswordRes)

// Build alicloud_db_instance resource.
alicloudDBInstanceRes, alicloudDBInstanceID, err := mysql.generateAlicloudDBInstance(
alicloudProviderCfg, region,
)
if err != nil {
return nil, nil, err
}
resources = append(resources, *alicloudDBInstanceRes)

// Build alicloud_db_connection resource.
var alicloudDBConnectionRes *apiv1.Resource
var alicloudDBConnectionID string
if IsPublicAccessible(mysql.SecurityIPs) {
alicloudDBConnectionRes, alicloudDBConnectionID, err = mysql.generateAlicloudDBConnection(
alicloudProviderCfg,
region, alicloudDBInstanceID,
)
if err != nil {
return nil, nil, err
}

resources = append(resources, *alicloudDBConnectionRes)
}

// Build alicloud_rds_account resuorce.
alicloudRDSAccountRes, err := mysql.generateAlicloudRDSAccount(
alicloudProviderCfg,
region, mysql.Username, randomPasswordID, alicloudDBInstanceID,
)
if err != nil {
return nil, nil, err
}
resources = append(resources, *alicloudRDSAccountRes)

hostAddress := modules.KusionPathDependency(alicloudDBInstanceID, "connection_string")
if !mysql.PrivateRouting {
// Set the public network connection string as the host address.
hostAddress = modules.KusionPathDependency(alicloudDBConnectionID, "connection_string")
}
password := modules.KusionPathDependency(randomPasswordID, "result")

// Build Kubernetes Secret with the hostAddress, username and password of the Alicloud provided MySQL instance,
// and inject the credentials as the environment variable patcher.
dbSecret, pathcer, err := mysql.GenerateDBSecret(request, hostAddress, mysql.Username, password)
if err != nil {
return nil, nil, err
}
resources = append(resources, *dbSecret)
patchers = append(patchers, *pathcer)

return resources, patchers, nil
}

// generateAlicloudDBInstance generates alicloud_db_instance resource
// for the Alicloud provided MySQL database instance.
func (mysql *MySQL) generateAlicloudDBInstance(alicloudProviderCfg apiv1.ProviderConfig,
region string,
) (*apiv1.Resource, string, error) {
resAttrs := map[string]interface{}{
"category": mysql.Category,
"engine": "MySQL",
"engine_version": mysql.Version,
"instance_storage": mysql.Size,
"instance_type": mysql.InstanceType,
"security_ips": mysql.SecurityIPs,
"vswitch_id": mysql.SubnetID,
"instance_name": mysql.DatabaseName,
}

// Set the serverless-specific attributes of the alicloud_db_instance resource.
if strings.Contains(mysql.Category, "serverless") {
resAttrs["db_instance_storage_type"] = "cloud_essd"
resAttrs["instance_charge_type"] = "Serverless"

serverlessConfig := alicloudServerlessConfig{
MaxCapacity: 8,
MinCapacity: 1,
}
serverlessConfig.AutoPause = false
serverlessConfig.SwitchForce = false

resAttrs["serverless_config"] = []alicloudServerlessConfig{
serverlessConfig,
}
}

id, err := module.TerraformResourceID(alicloudProviderCfg, alicloudDBInstance, mysql.DatabaseName)
if err != nil {
return nil, "", err
}

resExts, err := module.TerraformProviderExtensions(alicloudProviderCfg, map[string]any{"region": region}, alicloudDBInstance)
if err != nil {
return nil, "", err
}

resource, err := module.WrapTFResourceToKusionResource(id, resAttrs, resExts, nil)
if err != nil {
return nil, "", err
}

return resource, id, nil
}

// generateAlicloudDBConnection generates alicloud_db_connection resource
// for the Alicloud provided MySQL database instance.
func (mysql *MySQL) generateAlicloudDBConnection(alicloudProviderCfg apiv1.ProviderConfig,
region, dbInstanceID string,
) (*apiv1.Resource, string, error) {
resAttrs := map[string]interface{}{
"instance_id": modules.KusionPathDependency(dbInstanceID, "id"),
}

id, err := module.TerraformResourceID(alicloudProviderCfg, alicloudDBConnection, mysql.DatabaseName)
if err != nil {
return nil, "", err
}

resExts, err := module.TerraformProviderExtensions(alicloudProviderCfg, map[string]any{"region": region}, alicloudDBConnection)
if err != nil {
return nil, "", err
}

resource, err := module.WrapTFResourceToKusionResource(id, resAttrs, resExts, nil)
if err != nil {
return nil, "", err
}

return resource, id, nil
}

// generateAlicloudRDSAccount generates alicloud_rds_account resource
// for the Alicloud provided MySQL database instance.
func (mysql *MySQL) generateAlicloudRDSAccount(alicloudProviderCfg apiv1.ProviderConfig,
region, accountName, randomPasswordID, dbInstanceID string,
) (*apiv1.Resource, error) {
resAttrs := map[string]interface{}{
"account_name": accountName,
"account_password": modules.KusionPathDependency(randomPasswordID, "result"),
"account_type": "Super",
"db_instance_id": modules.KusionPathDependency(dbInstanceID, "id"),
}

id, err := module.TerraformResourceID(alicloudProviderCfg, alicloudRDSAccount, mysql.DatabaseName)
if err != nil {
return nil, err
}

resExts, err := module.TerraformProviderExtensions(alicloudProviderCfg, map[string]any{"region": region}, alicloudRDSAccount)
if err != nil {
return nil, err
}

resource, err := module.WrapTFResourceToKusionResource(id, resAttrs, resExts, nil)
if err != nil {
return nil, err
}

return resource, nil
}
Loading

0 comments on commit af9a793

Please sign in to comment.