Files
fission-console/terraform/provider/internal/resources/function_resource.go
T

236 lines
8.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package resources
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"terraform-provider-fission/internal/client"
)
var _ resource.Resource = &FunctionResource{}
// Изменено: 2026-04-14 20:00 UTC.
// Resource для управления Fission Function через Kubernetes CRD API.
type FunctionResource struct {
client *client.Client
}
// functionResourceModel описывает состояние terraform ресурса fission_function.
type functionResourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Environment types.String `tfsdk:"environment"`
PackageName types.String `tfsdk:"package_name"`
Entrypoint types.String `tfsdk:"entrypoint"`
Namespace types.String `tfsdk:"namespace"`
UID types.String `tfsdk:"uid"`
}
// NewFunctionResource создает инстанс ресурса функции.
func NewFunctionResource() resource.Resource {
return &FunctionResource{}
}
// Metadata задает имя ресурса fission_function.
func (r *FunctionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_function"
}
// Schema задает Terraform schema для Function.
func (r *FunctionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "Идентификатор ресурса (namespace/name).",
},
"name": schema.StringAttribute{
Required: true,
Description: "Имя Fission Function.",
},
"environment": schema.StringAttribute{
Required: true,
Description: "Имя Environment, в котором выполняется функция.",
},
"package_name": schema.StringAttribute{
Required: true,
Description: "Имя Fission Package с кодом функции.",
},
"entrypoint": schema.StringAttribute{
Required: true,
Description: "Имя точки входа в пакете (например main.main).",
},
"namespace": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Namespace, где создается Function. По умолчанию используется provider namespace.",
},
"uid": schema.StringAttribute{
Computed: true,
Description: "UID Kubernetes объекта Function.",
},
},
}
}
// Configure получает клиент из provider.Configure().
func (r *FunctionResource) 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 создает новый Function CRD объект.
func (r *FunctionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan functionResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
namespace := resolveNamespace(plan.Namespace, r.client.Namespace)
functionObject := functionToUnstructured(plan, namespace)
createdFunction, err := r.client.CreateFunction(ctx, functionObject)
if err != nil {
resp.Diagnostics.AddError("Ошибка создания Fission Function", err.Error())
return
}
state := unstructuredToFunctionModel(createdFunction, plan)
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
// Read синхронизирует Terraform state с текущим состоянием Function CRD.
func (r *FunctionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state functionResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
namespace := resolveNamespace(state.Namespace, r.client.Namespace)
functionObject, err := r.client.GetFunction(ctx, namespace, state.Name.ValueString())
if err != nil {
if client.IsNotFound(err) {
resp.State.RemoveResource(ctx)
return
}
resp.Diagnostics.AddError("Ошибка чтения Fission Function", err.Error())
return
}
updatedState := unstructuredToFunctionModel(functionObject, state)
resp.Diagnostics.Append(resp.State.Set(ctx, &updatedState)...)
}
// Update обновляет существующий Function CRD объект.
func (r *FunctionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan functionResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
namespace := resolveNamespace(plan.Namespace, r.client.Namespace)
existingFunction, err := r.client.GetFunction(ctx, namespace, plan.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка получения Function перед обновлением", err.Error())
return
}
functionObject := functionToUnstructured(plan, namespace)
functionObject.SetResourceVersion(existingFunction.GetResourceVersion())
updatedFunction, err := r.client.UpdateFunction(ctx, functionObject)
if err != nil {
resp.Diagnostics.AddError("Ошибка обновления Fission Function", err.Error())
return
}
state := unstructuredToFunctionModel(updatedFunction, plan)
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
// Delete удаляет Function CRD объект.
func (r *FunctionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state functionResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
namespace := resolveNamespace(state.Namespace, r.client.Namespace)
err := r.client.DeleteFunction(ctx, namespace, state.Name.ValueString())
if err != nil && !client.IsNotFound(err) {
resp.Diagnostics.AddError("Ошибка удаления Fission Function", err.Error())
}
}
// functionToUnstructured преобразует Terraform model в Kubernetes CRD payload.
func functionToUnstructured(model functionResourceModel, namespace string) *unstructured.Unstructured {
return &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "fission.io/v1",
"kind": "Function",
"metadata": map[string]interface{}{
"name": model.Name.ValueString(),
"namespace": namespace,
},
"spec": map[string]interface{}{
"environment": map[string]interface{}{
"name": model.Environment.ValueString(),
"namespace": namespace,
},
"package": map[string]interface{}{
"packageref": map[string]interface{}{
"name": model.PackageName.ValueString(),
"namespace": namespace,
},
"functionName": model.Entrypoint.ValueString(),
},
},
}}
}
// unstructuredToFunctionModel читает нужные поля из CRD объекта обратно в Terraform state.
func unstructuredToFunctionModel(functionObject *unstructured.Unstructured, base functionResourceModel) functionResourceModel {
environmentName, _, _ := unstructured.NestedString(functionObject.Object, "spec", "environment", "name")
packageName, _, _ := unstructured.NestedString(functionObject.Object, "spec", "package", "packageref", "name")
entrypoint, _, _ := unstructured.NestedString(functionObject.Object, "spec", "package", "functionName")
state := base
state.Name = types.StringValue(functionObject.GetName())
state.Namespace = types.StringValue(functionObject.GetNamespace())
state.ID = types.StringValue(fmt.Sprintf("%s/%s", functionObject.GetNamespace(), functionObject.GetName()))
state.UID = types.StringValue(string(functionObject.GetUID()))
if environmentName != "" {
state.Environment = types.StringValue(environmentName)
}
if packageName != "" {
state.PackageName = types.StringValue(packageName)
}
if entrypoint != "" {
state.Entrypoint = types.StringValue(entrypoint)
}
return state
}