fix(provider): detect source code drift and validate package/function references

This commit is contained in:
Naeel
2026-04-15 10:51:40 +03:00
parent e2b6a4472e
commit 1b8bff8364
6 changed files with 252 additions and 0 deletions
@@ -2,6 +2,7 @@ package resources
import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
@@ -19,6 +20,7 @@ import (
var _ resource.Resource = &PackageResource{}
var _ resource.ResourceWithImportState = &PackageResource{}
var _ resource.ResourceWithModifyPlan = &PackageResource{}
// Изменено: 2026-04-14 19:45 UTC.
// Resource для управления Fission Package через Kubernetes CRD API.
@@ -77,6 +79,7 @@ func (r *PackageResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
"code_hash": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Произвольный хеш кода для контроля изменений.",
},
"build_command": schema.StringAttribute{
@@ -104,6 +107,46 @@ func (r *PackageResource) Schema(_ context.Context, _ resource.SchemaRequest, re
}
}
// ModifyPlan пересчитывает code_hash по локальному коду, чтобы terraform видел изменения source_dir/code_path.
func (r *PackageResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
if req.Plan.Raw.IsNull() {
return
}
var plan packageResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
var config packageResourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
if hasManualCodeHash(config.CodeHash) {
return
}
if plan.SourceDir.IsUnknown() || plan.CodePath.IsUnknown() {
return
}
if !validatePackageSource(plan.SourceDir, plan.CodePath, &resp.Diagnostics) {
return
}
literalBytes, err := loadPackageLiteral(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка чтения исходного кода пакета", err.Error())
return
}
plan.CodeHash = types.StringValue(calculateCodeHash(literalBytes))
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
}
// Configure получает клиент из provider.Configure().
func (r *PackageResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
@@ -135,12 +178,21 @@ func (r *PackageResource) Create(ctx context.Context, req resource.CreateRequest
return
}
if err := ensureEnvironmentExists(ctx, r.client, namespace, plan.Environment.ValueString()); err != nil {
resp.Diagnostics.AddError("Ошибка валидации Environment для Package", err.Error())
return
}
literalBytes, err := loadPackageLiteral(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка чтения исходного кода пакета", err.Error())
return
}
if !hasManualCodeHash(plan.CodeHash) {
plan.CodeHash = types.StringValue(calculateCodeHash(literalBytes))
}
packageObject := packageToUnstructured(plan, namespace, literalBytes)
createdPackage, err := r.client.CreatePackage(ctx, packageObject)
if err != nil {
@@ -189,12 +241,21 @@ func (r *PackageResource) Update(ctx context.Context, req resource.UpdateRequest
return
}
if err := ensureEnvironmentExists(ctx, r.client, namespace, plan.Environment.ValueString()); err != nil {
resp.Diagnostics.AddError("Ошибка валидации Environment для Package", err.Error())
return
}
literalBytes, err := loadPackageLiteral(plan.SourceDir.ValueString(), plan.CodePath.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка чтения исходного кода пакета", err.Error())
return
}
if !hasManualCodeHash(plan.CodeHash) {
plan.CodeHash = types.StringValue(calculateCodeHash(literalBytes))
}
existingPackage, err := r.client.GetPackage(ctx, namespace, plan.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка получения Package перед обновлением", err.Error())
@@ -343,6 +404,7 @@ func unstructuredToPackageModel(packageObject *unstructured.Unstructured, base p
buildCommand, _, _ := unstructured.NestedString(packageObject.Object, "spec", "buildcmd")
buildStatus, _, _ := unstructured.NestedString(packageObject.Object, "status", "buildstatus")
buildLog, _, _ := unstructured.NestedString(packageObject.Object, "status", "buildlog")
deploymentLiteral, _, _ := unstructured.NestedString(packageObject.Object, "spec", "deployment", "literal")
state := packageResourceModel{
ID: types.StringValue(fmt.Sprintf("%s/%s", packageObject.GetNamespace(), packageObject.GetName())),
@@ -371,5 +433,20 @@ func unstructuredToPackageModel(packageObject *unstructured.Unstructured, base p
state.BuildLog = types.StringValue(buildLog)
}
if deploymentLiteral != "" {
if literalBytes, err := base64.StdEncoding.DecodeString(deploymentLiteral); err == nil {
state.CodeHash = types.StringValue(calculateCodeHash(literalBytes))
}
}
return state
}
func hasManualCodeHash(codeHash types.String) bool {
return !codeHash.IsNull() && !codeHash.IsUnknown() && codeHash.ValueString() != ""
}
func calculateCodeHash(literalBytes []byte) string {
sum := sha256.Sum256(literalBytes)
return fmt.Sprintf("%x", sum)
}