661 lines
23 KiB
Go
661 lines
23 KiB
Go
package resources
|
||
|
||
// simple_function_resource.go — составной ресурс fission_simple_function.
|
||
//
|
||
// Позволяет объявить serverless-функцию одним блоком HCL:
|
||
//
|
||
// resource "fission_simple_function" "health" {
|
||
// name = "health"
|
||
// runtime = "nodejs"
|
||
// code_dir = "./code/health"
|
||
// url = "/ecom/health"
|
||
// methods = ["GET"]
|
||
// }
|
||
//
|
||
// Провайдер сам:
|
||
// 1. Ищет environment simple-<runtime>-env, если не найден — создаёт.
|
||
// 2. Создаёт Package (<name>-pkg) с кодом из code_dir.
|
||
// 3. Создаёт Function (<name>).
|
||
// 4. Создаёт HTTPTrigger (<name>-trigger), если указан url.
|
||
//
|
||
// Добавлено: 2026-04-20
|
||
|
||
import (
|
||
"context"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"fmt"
|
||
|
||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
|
||
"terraform-provider-fission/internal/client"
|
||
)
|
||
|
||
var _ resource.Resource = &SimpleFunctionResource{}
|
||
var _ resource.ResourceWithImportState = &SimpleFunctionResource{}
|
||
var _ resource.ResourceWithModifyPlan = &SimpleFunctionResource{}
|
||
|
||
// runtimeMeta содержит параметры окружения для каждого поддерживаемого runtime.
|
||
type runtimeMeta struct {
|
||
image string // образ среды выполнения
|
||
builderImage string // образ сборщика (пустая строка если не нужен)
|
||
deployType string // "literal" или "source"
|
||
entrypoint string // точка входа функции
|
||
poolSize int64 // размер пула
|
||
}
|
||
|
||
// runtimeRegistry — маппинг имени runtime → параметры среды выполнения.
|
||
var runtimeRegistry = map[string]runtimeMeta{
|
||
"nodejs": {
|
||
image: "ghcr.io/fission/node-env:latest",
|
||
builderImage: "",
|
||
deployType: "literal",
|
||
entrypoint: "", // JS runtime использует default export (пустая строка)
|
||
poolSize: 3,
|
||
},
|
||
"python": {
|
||
image: "ghcr.io/fission/python-env:latest",
|
||
builderImage: "",
|
||
deployType: "literal",
|
||
entrypoint: "main.main",
|
||
poolSize: 3,
|
||
},
|
||
"go": {
|
||
image: "ghcr.io/fission/go-env:latest",
|
||
builderImage: "ghcr.io/fission/go-builder:latest",
|
||
deployType: "source",
|
||
entrypoint: "Handler",
|
||
poolSize: 3,
|
||
},
|
||
"ruby": {
|
||
image: "ghcr.io/fission/ruby-env:latest",
|
||
builderImage: "",
|
||
deployType: "literal",
|
||
entrypoint: "main.main",
|
||
poolSize: 3,
|
||
},
|
||
"php": {
|
||
image: "ghcr.io/fission/php-env:latest",
|
||
builderImage: "",
|
||
deployType: "literal",
|
||
entrypoint: "main.main",
|
||
poolSize: 3,
|
||
},
|
||
"perl": {
|
||
image: "ghcr.io/fission/perl-env:latest",
|
||
builderImage: "",
|
||
deployType: "literal",
|
||
entrypoint: "main.main",
|
||
poolSize: 3,
|
||
},
|
||
}
|
||
|
||
// SimpleFunctionResource — составной ресурс: Environment + Package + Function + HTTPTrigger.
|
||
type SimpleFunctionResource struct {
|
||
client *client.Client
|
||
}
|
||
|
||
// simpleFunctionModel описывает Terraform state ресурса fission_simple_function.
|
||
type simpleFunctionModel struct {
|
||
ID types.String `tfsdk:"id"`
|
||
Name types.String `tfsdk:"name"`
|
||
Runtime types.String `tfsdk:"runtime"`
|
||
CodeDir types.String `tfsdk:"code_dir"`
|
||
URL types.String `tfsdk:"url"`
|
||
Methods types.List `tfsdk:"methods"`
|
||
Namespace types.String `tfsdk:"namespace"`
|
||
EnvironmentName types.String `tfsdk:"environment_name"`
|
||
PackageName types.String `tfsdk:"package_name"`
|
||
TriggerName types.String `tfsdk:"trigger_name"`
|
||
CodeHash types.String `tfsdk:"code_hash"`
|
||
}
|
||
|
||
// NewSimpleFunctionResource создаёт инстанс составного ресурса.
|
||
func NewSimpleFunctionResource() resource.Resource {
|
||
return &SimpleFunctionResource{}
|
||
}
|
||
|
||
func (r *SimpleFunctionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||
resp.TypeName = req.ProviderTypeName + "_simple_function"
|
||
}
|
||
|
||
func (r *SimpleFunctionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||
resp.Schema = schema.Schema{
|
||
Description: "Составной ресурс: создаёт Environment (при необходимости), Package, Function и HTTPTrigger одним блоком.",
|
||
Attributes: map[string]schema.Attribute{
|
||
"id": schema.StringAttribute{
|
||
Computed: true,
|
||
Description: "Идентификатор ресурса (namespace/name).",
|
||
},
|
||
"name": schema.StringAttribute{
|
||
Required: true,
|
||
Description: "Имя функции. Используется как база для имён Package и HTTPTrigger.",
|
||
PlanModifiers: []planmodifier.String{
|
||
stringplanmodifier.RequiresReplace(),
|
||
},
|
||
},
|
||
"runtime": schema.StringAttribute{
|
||
Required: true,
|
||
Description: "Runtime функции: nodejs, python, go, ruby, php, perl.",
|
||
PlanModifiers: []planmodifier.String{
|
||
stringplanmodifier.RequiresReplace(),
|
||
},
|
||
},
|
||
"code_dir": schema.StringAttribute{
|
||
Required: true,
|
||
Description: "Путь к директории с исходным кодом (main.js, main.py, main.go и т.д.).",
|
||
},
|
||
"url": schema.StringAttribute{
|
||
Optional: true,
|
||
Description: "Относительный URL для HTTPTrigger. Если не задан — HTTPTrigger не создаётся.",
|
||
},
|
||
"methods": schema.ListAttribute{
|
||
Optional: true,
|
||
Computed: true,
|
||
ElementType: types.StringType,
|
||
Description: "HTTP методы для HTTPTrigger. По умолчанию [\"GET\"].",
|
||
},
|
||
"namespace": schema.StringAttribute{
|
||
Optional: true,
|
||
Computed: true,
|
||
Description: "Namespace для ресурсов. По умолчанию используется namespace провайдера.",
|
||
},
|
||
"environment_name": schema.StringAttribute{
|
||
Computed: true,
|
||
Description: "Имя Environment, который был использован или создан (simple-<runtime>-env).",
|
||
},
|
||
"package_name": schema.StringAttribute{
|
||
Computed: true,
|
||
Description: "Имя созданного Package (<name>-pkg).",
|
||
},
|
||
"trigger_name": schema.StringAttribute{
|
||
Computed: true,
|
||
Description: "Имя созданного HTTPTrigger (<name>-trigger). Пусто, если url не задан.",
|
||
},
|
||
"code_hash": schema.StringAttribute{
|
||
Computed: true,
|
||
Description: "SHA-256 хеш кода из code_dir. Terraform обнаруживает изменения кода по этому полю.",
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// ModifyPlan пересчитывает code_hash из code_dir, чтобы Terraform видел изменения кода.
|
||
func (r *SimpleFunctionResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
|
||
if req.Plan.Raw.IsNull() {
|
||
return
|
||
}
|
||
|
||
var plan simpleFunctionModel
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||
if resp.Diagnostics.HasError() {
|
||
return
|
||
}
|
||
|
||
if plan.CodeDir.IsNull() || plan.CodeDir.IsUnknown() || plan.CodeDir.ValueString() == "" {
|
||
return
|
||
}
|
||
|
||
deployType := "literal"
|
||
if !plan.Runtime.IsNull() && !plan.Runtime.IsUnknown() {
|
||
if meta, ok := runtimeRegistry[plan.Runtime.ValueString()]; ok {
|
||
deployType = meta.deployType
|
||
}
|
||
}
|
||
|
||
codeBytes, err := loadPackageContent(plan.CodeDir.ValueString(), "", deployType)
|
||
if err != nil {
|
||
// Файл может отсутствовать при первом plan — не ошибка.
|
||
return
|
||
}
|
||
|
||
sum := sha256.Sum256(codeBytes)
|
||
plan.CodeHash = types.StringValue(fmt.Sprintf("%x", sum))
|
||
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
|
||
}
|
||
|
||
// Configure получает клиент из provider.Configure().
|
||
func (r *SimpleFunctionResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||
if req.ProviderData == nil {
|
||
return
|
||
}
|
||
|
||
fissionClient, ok := req.ProviderData.(*client.Client)
|
||
if !ok {
|
||
resp.Diagnostics.AddError(
|
||
"Некорректный тип provider data",
|
||
fmt.Sprintf("Ожидался *client.Client, получен: %T", req.ProviderData),
|
||
)
|
||
return
|
||
}
|
||
|
||
r.client = fissionClient
|
||
}
|
||
|
||
// Create создаёт Environment (при необходимости), Package, Function и HTTPTrigger.
|
||
func (r *SimpleFunctionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||
var plan simpleFunctionModel
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||
if resp.Diagnostics.HasError() {
|
||
return
|
||
}
|
||
|
||
namespace := resolveNamespace(plan.Namespace, r.client.Namespace)
|
||
|
||
meta, ok := runtimeRegistry[plan.Runtime.ValueString()]
|
||
if !ok {
|
||
resp.Diagnostics.AddError(
|
||
"Неподдерживаемый runtime",
|
||
fmt.Sprintf("Runtime %q не поддерживается. Допустимые значения: nodejs, python, go, ruby, php, perl.", plan.Runtime.ValueString()),
|
||
)
|
||
return
|
||
}
|
||
|
||
// 1. Найти или создать environment.
|
||
envName, err := findOrCreateSimpleEnv(ctx, r.client, namespace, plan.Runtime.ValueString(), meta)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Ошибка подготовки Environment", err.Error())
|
||
return
|
||
}
|
||
|
||
// 2. Загрузить код из code_dir.
|
||
codeBytes, err := loadPackageContent(plan.CodeDir.ValueString(), "", meta.deployType)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Ошибка чтения исходного кода", err.Error())
|
||
return
|
||
}
|
||
sum := sha256.Sum256(codeBytes)
|
||
codeHash := fmt.Sprintf("%x", sum)
|
||
|
||
// 3. Создать Package.
|
||
pkgName := plan.Name.ValueString() + "-pkg"
|
||
pkgObj := buildSimplePackage(pkgName, envName, namespace, codeBytes, meta.deployType)
|
||
if _, err = r.client.CreatePackage(ctx, pkgObj); err != nil {
|
||
resp.Diagnostics.AddError("Ошибка создания Package", err.Error())
|
||
return
|
||
}
|
||
|
||
// 4. Создать Function.
|
||
fnObj := buildSimpleFunction(plan.Name.ValueString(), envName, pkgName, meta.entrypoint, namespace)
|
||
if _, err = r.client.CreateFunction(ctx, fnObj); err != nil {
|
||
_ = r.client.DeletePackage(ctx, namespace, pkgName)
|
||
resp.Diagnostics.AddError("Ошибка создания Function", err.Error())
|
||
return
|
||
}
|
||
|
||
// 5. Создать HTTPTrigger (только если задан url).
|
||
triggerName := ""
|
||
if url := plan.URL.ValueString(); url != "" {
|
||
triggerName = plan.Name.ValueString() + "-trigger"
|
||
methods := simpleMethods(ctx, plan.Methods)
|
||
triggerObj := buildSimpleTrigger(triggerName, plan.Name.ValueString(), url, methods, namespace)
|
||
if _, err = r.client.CreateHTTPTrigger(ctx, triggerObj); err != nil {
|
||
_ = r.client.DeleteFunction(ctx, namespace, plan.Name.ValueString())
|
||
_ = r.client.DeletePackage(ctx, namespace, pkgName)
|
||
resp.Diagnostics.AddError("Ошибка создания HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
}
|
||
|
||
defaultMethods, _ := types.ListValueFrom(ctx, types.StringType, []string{"GET"})
|
||
methods := plan.Methods
|
||
if methods.IsNull() || methods.IsUnknown() {
|
||
methods = defaultMethods
|
||
}
|
||
|
||
state := simpleFunctionModel{
|
||
ID: types.StringValue(fmt.Sprintf("%s/%s", namespace, plan.Name.ValueString())),
|
||
Name: plan.Name,
|
||
Runtime: plan.Runtime,
|
||
CodeDir: plan.CodeDir,
|
||
URL: plan.URL,
|
||
Methods: methods,
|
||
Namespace: types.StringValue(namespace),
|
||
EnvironmentName: types.StringValue(envName),
|
||
PackageName: types.StringValue(pkgName),
|
||
TriggerName: types.StringValue(triggerName),
|
||
CodeHash: types.StringValue(codeHash),
|
||
}
|
||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||
}
|
||
|
||
// Read синхронизирует state с Kubernetes.
|
||
func (r *SimpleFunctionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||
var state simpleFunctionModel
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||
if resp.Diagnostics.HasError() {
|
||
return
|
||
}
|
||
|
||
namespace := resolveNamespace(state.Namespace, r.client.Namespace)
|
||
|
||
// Основной ресурс — Function.
|
||
if _, err := r.client.GetFunction(ctx, namespace, state.Name.ValueString()); err != nil {
|
||
if client.IsNotFound(err) {
|
||
resp.State.RemoveResource(ctx)
|
||
return
|
||
}
|
||
resp.Diagnostics.AddError("Ошибка чтения Function", err.Error())
|
||
return
|
||
}
|
||
|
||
// Синхронизировать url/methods из реального состояния HTTPTrigger.
|
||
if triggerName := state.TriggerName.ValueString(); triggerName != "" {
|
||
triggerObj, err := r.client.GetHTTPTrigger(ctx, namespace, triggerName)
|
||
if err != nil && !client.IsNotFound(err) {
|
||
resp.Diagnostics.AddError("Ошибка чтения HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
if err == nil {
|
||
if urlVal, _, _ := unstructured.NestedString(triggerObj.Object, "spec", "relativeurl"); urlVal != "" {
|
||
state.URL = types.StringValue(urlVal)
|
||
}
|
||
if methodsVal, _, _ := unstructured.NestedStringSlice(triggerObj.Object, "spec", "methods"); len(methodsVal) > 0 {
|
||
methodsList, _ := types.ListValueFrom(ctx, types.StringType, methodsVal)
|
||
state.Methods = methodsList
|
||
}
|
||
}
|
||
}
|
||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||
}
|
||
|
||
// Update обновляет Package (если код изменился) и HTTPTrigger (если url/methods изменились).
|
||
func (r *SimpleFunctionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||
var plan simpleFunctionModel
|
||
var state simpleFunctionModel
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||
if resp.Diagnostics.HasError() {
|
||
return
|
||
}
|
||
|
||
namespace := resolveNamespace(plan.Namespace, r.client.Namespace)
|
||
meta := runtimeRegistry[plan.Runtime.ValueString()]
|
||
|
||
// Обновить Package, если изменился код.
|
||
newCodeBytes, err := loadPackageContent(plan.CodeDir.ValueString(), "", meta.deployType)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Ошибка чтения исходного кода", err.Error())
|
||
return
|
||
}
|
||
sum := sha256.Sum256(newCodeBytes)
|
||
newHash := fmt.Sprintf("%x", sum)
|
||
|
||
if newHash != state.CodeHash.ValueString() {
|
||
pkgName := state.PackageName.ValueString()
|
||
existingPkg, err := r.client.GetPackage(ctx, namespace, pkgName)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Ошибка получения Package перед обновлением", err.Error())
|
||
return
|
||
}
|
||
newPkg := buildSimplePackage(pkgName, state.EnvironmentName.ValueString(), namespace, newCodeBytes, meta.deployType)
|
||
newPkg.SetResourceVersion(existingPkg.GetResourceVersion())
|
||
if _, err = r.client.UpdatePackage(ctx, newPkg); err != nil {
|
||
resp.Diagnostics.AddError("Ошибка обновления Package", err.Error())
|
||
return
|
||
}
|
||
}
|
||
|
||
// Обновить HTTPTrigger при изменении url или methods.
|
||
oldURL := state.URL.ValueString()
|
||
newURL := plan.URL.ValueString()
|
||
triggerName := state.TriggerName.ValueString()
|
||
|
||
switch {
|
||
case oldURL == "" && newURL != "":
|
||
triggerName = plan.Name.ValueString() + "-trigger"
|
||
methods := simpleMethods(ctx, plan.Methods)
|
||
triggerObj := buildSimpleTrigger(triggerName, plan.Name.ValueString(), newURL, methods, namespace)
|
||
if _, err = r.client.CreateHTTPTrigger(ctx, triggerObj); err != nil {
|
||
resp.Diagnostics.AddError("Ошибка создания HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
|
||
case oldURL != "" && newURL == "":
|
||
if triggerName != "" {
|
||
if err := r.client.DeleteHTTPTrigger(ctx, namespace, triggerName); err != nil && !client.IsNotFound(err) {
|
||
resp.Diagnostics.AddError("Ошибка удаления HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
}
|
||
triggerName = ""
|
||
|
||
case oldURL != "" && newURL != "":
|
||
if triggerName == "" {
|
||
triggerName = plan.Name.ValueString() + "-trigger"
|
||
}
|
||
methods := simpleMethods(ctx, plan.Methods)
|
||
existing, err := r.client.GetHTTPTrigger(ctx, namespace, triggerName)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Ошибка получения HTTPTrigger перед обновлением", err.Error())
|
||
return
|
||
}
|
||
newTrigger := buildSimpleTrigger(triggerName, plan.Name.ValueString(), newURL, methods, namespace)
|
||
newTrigger.SetResourceVersion(existing.GetResourceVersion())
|
||
if _, err = r.client.UpdateHTTPTrigger(ctx, newTrigger); err != nil {
|
||
resp.Diagnostics.AddError("Ошибка обновления HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
}
|
||
|
||
defaultMethods, _ := types.ListValueFrom(ctx, types.StringType, []string{"GET"})
|
||
methods := plan.Methods
|
||
if methods.IsNull() || methods.IsUnknown() {
|
||
methods = defaultMethods
|
||
}
|
||
|
||
newState := state
|
||
newState.CodeDir = plan.CodeDir
|
||
newState.URL = plan.URL
|
||
newState.Methods = methods
|
||
newState.CodeHash = types.StringValue(newHash)
|
||
newState.TriggerName = types.StringValue(triggerName)
|
||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...)
|
||
}
|
||
|
||
// Delete удаляет HTTPTrigger, Function и Package (Environment не удаляется).
|
||
func (r *SimpleFunctionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||
var state simpleFunctionModel
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||
if resp.Diagnostics.HasError() {
|
||
return
|
||
}
|
||
|
||
namespace := resolveNamespace(state.Namespace, r.client.Namespace)
|
||
|
||
if triggerName := state.TriggerName.ValueString(); triggerName != "" {
|
||
if err := r.client.DeleteHTTPTrigger(ctx, namespace, triggerName); err != nil && !client.IsNotFound(err) {
|
||
resp.Diagnostics.AddError("Ошибка удаления HTTPTrigger", err.Error())
|
||
return
|
||
}
|
||
}
|
||
|
||
if err := r.client.DeleteFunction(ctx, namespace, state.Name.ValueString()); err != nil && !client.IsNotFound(err) {
|
||
resp.Diagnostics.AddError("Ошибка удаления Function", err.Error())
|
||
return
|
||
}
|
||
|
||
if pkgName := state.PackageName.ValueString(); pkgName != "" {
|
||
if err := r.client.DeletePackage(ctx, namespace, pkgName); err != nil && !client.IsNotFound(err) {
|
||
resp.Diagnostics.AddError("Ошибка удаления Package", err.Error())
|
||
}
|
||
}
|
||
}
|
||
|
||
// ImportState поддерживает импорт в формате namespace/name.
|
||
func (r *SimpleFunctionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||
namespace, name, err := parseImportID(req.ID)
|
||
if err != nil {
|
||
resp.Diagnostics.AddError("Некорректный формат import ID", err.Error())
|
||
return
|
||
}
|
||
|
||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...)
|
||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("namespace"), namespace)...)
|
||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), name)...)
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Вспомогательные функции
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// findOrCreateSimpleEnv ищет environment simple-<runtime>-env; при отсутствии создаёт.
|
||
func findOrCreateSimpleEnv(ctx context.Context, fissionClient *client.Client, namespace, runtime string, meta runtimeMeta) (string, error) {
|
||
envName := fmt.Sprintf("simple-%s-env", runtime)
|
||
|
||
_, err := fissionClient.GetEnvironment(ctx, namespace, envName)
|
||
if err == nil {
|
||
return envName, nil
|
||
}
|
||
|
||
if !client.IsNotFound(err) {
|
||
return "", fmt.Errorf("проверка environment %q: %w", envName, err)
|
||
}
|
||
|
||
spec := map[string]interface{}{
|
||
"version": int64(2),
|
||
"runtime": map[string]interface{}{
|
||
"image": meta.image,
|
||
},
|
||
"poolsize": meta.poolSize,
|
||
}
|
||
if meta.builderImage != "" {
|
||
spec["builder"] = map[string]interface{}{
|
||
"image": meta.builderImage,
|
||
}
|
||
}
|
||
|
||
envObj := &unstructured.Unstructured{Object: map[string]interface{}{
|
||
"apiVersion": "fission.io/v1",
|
||
"kind": "Environment",
|
||
"metadata": map[string]interface{}{
|
||
"name": envName,
|
||
"namespace": namespace,
|
||
},
|
||
"spec": spec,
|
||
}}
|
||
|
||
if _, err = fissionClient.CreateEnvironment(ctx, envObj); err != nil {
|
||
return "", fmt.Errorf("создание environment %q: %w", envName, err)
|
||
}
|
||
|
||
return envName, nil
|
||
}
|
||
|
||
// buildSimplePackage формирует CRD объект Package с кодом в base64.
|
||
func buildSimplePackage(pkgName, envName, namespace string, codeBytes []byte, deployType string) *unstructured.Unstructured {
|
||
literalSource := base64.StdEncoding.EncodeToString(codeBytes)
|
||
|
||
spec := map[string]interface{}{
|
||
"environment": map[string]interface{}{
|
||
"name": envName,
|
||
"namespace": namespace,
|
||
},
|
||
}
|
||
|
||
if deployType == "source" {
|
||
spec["source"] = map[string]interface{}{
|
||
"type": "literal",
|
||
"literal": literalSource,
|
||
}
|
||
} else {
|
||
spec["deployment"] = map[string]interface{}{
|
||
"type": "literal",
|
||
"literal": literalSource,
|
||
}
|
||
}
|
||
|
||
return &unstructured.Unstructured{Object: map[string]interface{}{
|
||
"apiVersion": "fission.io/v1",
|
||
"kind": "Package",
|
||
"metadata": map[string]interface{}{
|
||
"name": pkgName,
|
||
"namespace": namespace,
|
||
},
|
||
"spec": spec,
|
||
}}
|
||
}
|
||
|
||
// buildSimpleFunction формирует CRD объект Function.
|
||
func buildSimpleFunction(name, envName, pkgName, entrypoint, namespace string) *unstructured.Unstructured {
|
||
return &unstructured.Unstructured{Object: map[string]interface{}{
|
||
"apiVersion": "fission.io/v1",
|
||
"kind": "Function",
|
||
"metadata": map[string]interface{}{
|
||
"name": name,
|
||
"namespace": namespace,
|
||
},
|
||
"spec": map[string]interface{}{
|
||
"environment": map[string]interface{}{
|
||
"name": envName,
|
||
"namespace": namespace,
|
||
},
|
||
"InvokeStrategy": map[string]interface{}{
|
||
"ExecutionStrategy": map[string]interface{}{
|
||
"ExecutorType": "poolmgr",
|
||
},
|
||
"StrategyType": "execution",
|
||
},
|
||
"package": map[string]interface{}{
|
||
"packageref": map[string]interface{}{
|
||
"name": pkgName,
|
||
"namespace": namespace,
|
||
},
|
||
"functionName": entrypoint,
|
||
},
|
||
},
|
||
}}
|
||
}
|
||
|
||
// buildSimpleTrigger формирует CRD объект HTTPTrigger.
|
||
func buildSimpleTrigger(triggerName, functionName, url string, methods []string, namespace string) *unstructured.Unstructured {
|
||
if len(methods) == 0 {
|
||
methods = []string{"GET"}
|
||
}
|
||
|
||
return &unstructured.Unstructured{Object: map[string]interface{}{
|
||
"apiVersion": "fission.io/v1",
|
||
"kind": "HTTPTrigger",
|
||
"metadata": map[string]interface{}{
|
||
"name": triggerName,
|
||
"namespace": namespace,
|
||
},
|
||
"spec": map[string]interface{}{
|
||
"relativeurl": url,
|
||
"methods": methods,
|
||
"functionref": map[string]interface{}{
|
||
"type": "name",
|
||
"name": functionName,
|
||
},
|
||
"createingress": false,
|
||
},
|
||
}}
|
||
}
|
||
|
||
// simpleMethods извлекает []string из types.List, возвращая ["GET"] по умолчанию.
|
||
func simpleMethods(ctx context.Context, methodsList types.List) []string {
|
||
if methodsList.IsNull() || methodsList.IsUnknown() || len(methodsList.Elements()) == 0 {
|
||
return []string{"GET"}
|
||
}
|
||
|
||
var methods []string
|
||
if diags := methodsList.ElementsAs(ctx, &methods, false); diags.HasError() {
|
||
return []string{"GET"}
|
||
}
|
||
|
||
if len(methods) == 0 {
|
||
return []string{"GET"}
|
||
}
|
||
|
||
return methods
|
||
}
|