feat: complete MVP resources and validate end-to-end terraform flow
This commit is contained in:
@@ -198,6 +198,12 @@ func functionToUnstructured(model functionResourceModel, namespace string) *unst
|
||||
"name": model.Environment.ValueString(),
|
||||
"namespace": namespace,
|
||||
},
|
||||
"InvokeStrategy": map[string]interface{}{
|
||||
"ExecutionStrategy": map[string]interface{}{
|
||||
"ExecutorType": "poolmgr",
|
||||
},
|
||||
"StrategyType": "execution",
|
||||
},
|
||||
"package": map[string]interface{}{
|
||||
"packageref": map[string]interface{}{
|
||||
"name": model.PackageName.ValueString(),
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
@@ -65,11 +66,14 @@ func (r *HTTPTriggerResource) Schema(_ context.Context, _ resource.SchemaRequest
|
||||
},
|
||||
"methods": schema.ListAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ElementType: types.StringType,
|
||||
Description: "HTTP методы trigger, например [\"GET\", \"POST\"].",
|
||||
},
|
||||
"create_ingress": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: booldefault.StaticBool(false),
|
||||
Description: "Включает создание ingress правила в Fission.",
|
||||
},
|
||||
"host": schema.StringAttribute{
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -137,13 +133,13 @@ func (r *PackageResource) Create(ctx context.Context, req resource.CreateRequest
|
||||
return
|
||||
}
|
||||
|
||||
archiveBytes, err := loadPackageArchive(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
|
||||
literalBytes, err := loadPackageLiteral(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка чтения исходного кода пакета", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
packageObject := packageToUnstructured(plan, namespace, archiveBytes)
|
||||
packageObject := packageToUnstructured(plan, namespace, literalBytes)
|
||||
createdPackage, err := r.client.CreatePackage(ctx, packageObject)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка создания Fission Package", err.Error())
|
||||
@@ -191,7 +187,7 @@ func (r *PackageResource) Update(ctx context.Context, req resource.UpdateRequest
|
||||
return
|
||||
}
|
||||
|
||||
archiveBytes, err := loadPackageArchive(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
|
||||
literalBytes, err := loadPackageLiteral(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка чтения исходного кода пакета", err.Error())
|
||||
return
|
||||
@@ -203,7 +199,7 @@ func (r *PackageResource) Update(ctx context.Context, req resource.UpdateRequest
|
||||
return
|
||||
}
|
||||
|
||||
packageObject := packageToUnstructured(plan, namespace, archiveBytes)
|
||||
packageObject := packageToUnstructured(plan, namespace, literalBytes)
|
||||
packageObject.SetResourceVersion(existingPackage.GetResourceVersion())
|
||||
|
||||
updatedPackage, err := r.client.UpdatePackage(ctx, packageObject)
|
||||
@@ -257,75 +253,29 @@ func resolveNamespace(resourceNamespace types.String, providerNamespace string)
|
||||
return namespace
|
||||
}
|
||||
|
||||
// loadPackageArchive читает zip из code_path или собирает zip из source_dir.
|
||||
func loadPackageArchive(sourceDir, codePath string) ([]byte, error) {
|
||||
// loadPackageLiteral читает bytes для spec.deployment.literal.
|
||||
func loadPackageLiteral(sourceDir, codePath string) ([]byte, error) {
|
||||
if sourceDir != "" {
|
||||
return zipDirectory(sourceDir)
|
||||
mainFilePath := filepath.Join(sourceDir, "main.py")
|
||||
mainFileBytes, err := os.ReadFile(mainFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read source_dir main.py %q: %w", mainFilePath, err)
|
||||
}
|
||||
|
||||
return mainFileBytes, nil
|
||||
}
|
||||
|
||||
archiveBytes, err := os.ReadFile(codePath)
|
||||
literalBytes, err := os.ReadFile(codePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read code_path %q: %w", codePath, err)
|
||||
}
|
||||
|
||||
return archiveBytes, nil
|
||||
}
|
||||
|
||||
// zipDirectory собирает zip-архив из директории для передачи в spec.source.literal.
|
||||
func zipDirectory(sourceDir string) ([]byte, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
zipWriter := zip.NewWriter(buffer)
|
||||
|
||||
walkError := filepath.WalkDir(sourceDir, func(path string, entry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
relativePath, err := filepath.Rel(sourceDir, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build relative path for %q: %w", path, err)
|
||||
}
|
||||
|
||||
zipPath := filepath.ToSlash(relativePath)
|
||||
fileWriter, err := zipWriter.Create(zipPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create zip entry %q: %w", zipPath, err)
|
||||
}
|
||||
|
||||
fileHandle, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open source file %q: %w", path, err)
|
||||
}
|
||||
|
||||
if _, err = io.Copy(fileWriter, fileHandle); err != nil {
|
||||
_ = fileHandle.Close()
|
||||
return fmt.Errorf("write zip entry %q: %w", zipPath, err)
|
||||
}
|
||||
|
||||
if err = fileHandle.Close(); err != nil {
|
||||
return fmt.Errorf("close source file %q: %w", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if walkError != nil {
|
||||
_ = zipWriter.Close()
|
||||
return nil, fmt.Errorf("zip source_dir %q: %w", sourceDir, walkError)
|
||||
}
|
||||
|
||||
if err := zipWriter.Close(); err != nil {
|
||||
return nil, fmt.Errorf("finalize zip archive: %w", err)
|
||||
}
|
||||
|
||||
return buffer.Bytes(), nil
|
||||
return literalBytes, nil
|
||||
}
|
||||
|
||||
// packageToUnstructured преобразует Terraform model в Kubernetes CRD payload.
|
||||
func packageToUnstructured(model packageResourceModel, namespace string, archiveBytes []byte) *unstructured.Unstructured {
|
||||
literalSource := base64.StdEncoding.EncodeToString(archiveBytes)
|
||||
func packageToUnstructured(model packageResourceModel, namespace string, literalBytes []byte) *unstructured.Unstructured {
|
||||
literalSource := base64.StdEncoding.EncodeToString(literalBytes)
|
||||
|
||||
object := map[string]interface{}{
|
||||
"apiVersion": "fission.io/v1",
|
||||
@@ -335,14 +285,15 @@ func packageToUnstructured(model packageResourceModel, namespace string, archive
|
||||
"namespace": namespace,
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"deployment": map[string]interface{}{
|
||||
"type": "literal",
|
||||
"literal": literalSource,
|
||||
},
|
||||
"environment": map[string]interface{}{
|
||||
"name": model.Environment.ValueString(),
|
||||
"namespace": namespace,
|
||||
},
|
||||
"source": map[string]interface{}{
|
||||
"type": "literal",
|
||||
"literal": literalSource,
|
||||
},
|
||||
"source": map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -360,11 +311,19 @@ func unstructuredToPackageModel(packageObject *unstructured.Unstructured, base p
|
||||
buildStatus, _, _ := unstructured.NestedString(packageObject.Object, "status", "buildstatus")
|
||||
buildLog, _, _ := unstructured.NestedString(packageObject.Object, "status", "buildlog")
|
||||
|
||||
state := base
|
||||
state.Name = types.StringValue(packageObject.GetName())
|
||||
state.Namespace = types.StringValue(packageObject.GetNamespace())
|
||||
state.ID = types.StringValue(fmt.Sprintf("%s/%s", packageObject.GetNamespace(), packageObject.GetName()))
|
||||
state.UID = types.StringValue(string(packageObject.GetUID()))
|
||||
state := packageResourceModel{
|
||||
ID: types.StringValue(fmt.Sprintf("%s/%s", packageObject.GetNamespace(), packageObject.GetName())),
|
||||
Name: types.StringValue(packageObject.GetName()),
|
||||
Environment: base.Environment,
|
||||
SourceDir: base.SourceDir,
|
||||
CodePath: base.CodePath,
|
||||
CodeHash: base.CodeHash,
|
||||
BuildCmd: base.BuildCmd,
|
||||
Namespace: types.StringValue(packageObject.GetNamespace()),
|
||||
UID: types.StringValue(string(packageObject.GetUID())),
|
||||
BuildStatus: types.StringNull(),
|
||||
BuildLog: types.StringNull(),
|
||||
}
|
||||
|
||||
if environmentName != "" {
|
||||
state.Environment = types.StringValue(environmentName)
|
||||
|
||||
Reference in New Issue
Block a user