feat: multi-language support — PHP, Ruby, Perl, Go (binary-env)
- PHP: ghcr.io/fission/php-env, entrypoint main.php::handler
- Ruby: ghcr.io/fission/ruby-env, entrypoint handler (single-word)
- Perl: ghcr.io/fission/perl-env (version=1), sub {} return value
- Go: ghcr.io/fission/binary-env with pre-compiled binary (shell script placeholder)
Provider changes:
- package_resource: added main.php, main.rb, main.pl to source_dir candidates
- function_resource: relaxed entrypoint validation to allow PHP (::), Ruby, Perl (single-word)
- function_resource_test: updated BadFormat test to reflect new valid formats
This commit is contained in:
@@ -8,6 +8,9 @@ bin/
|
||||
*.test
|
||||
*.out
|
||||
|
||||
# Compiled function binaries (generated, not versioned)
|
||||
examples/*/dist/
|
||||
|
||||
# Provider binaries
|
||||
terraform-provider-fission
|
||||
terraform-provider-fission_*
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Handler используется binary-env: binary получает HTTP request через stdin,
|
||||
// stdout является HTTP response body.
|
||||
//
|
||||
// Для деплоя нужно скомпилировать перед terraform apply:
|
||||
// CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o ../dist/handler .
|
||||
//
|
||||
// Compiled size: ~1.6MB (слишком велико для literal deployment).
|
||||
// Используется shell-based handler в dist/handler (см. ниже).
|
||||
func main() {
|
||||
fmt.Print("Hello from Go in Fission")
|
||||
}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "Hello from Go in Fission")
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
fission = {
|
||||
source = "nail/fission"
|
||||
version = "~> 0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "fission" {
|
||||
kubeconfig_path = "/home/naeel/.kube/config"
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
# Go использует binary-env: функция является скомпилированным Linux-бинарником.
|
||||
# Перед apply необходимо скомпилировать:
|
||||
# CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o dist/handler code/
|
||||
resource "fission_environment" "go" {
|
||||
name = "tf-go-hello-env"
|
||||
image = "ghcr.io/fission/binary-env"
|
||||
version = 3
|
||||
}
|
||||
|
||||
resource "fission_package" "pkg" {
|
||||
name = "tf-go-hello-pkg"
|
||||
environment = fission_environment.go.name
|
||||
code_path = "${path.module}/dist/handler"
|
||||
}
|
||||
|
||||
resource "fission_function" "fn" {
|
||||
name = "tf-go-hello-fn"
|
||||
environment = fission_environment.go.name
|
||||
package_name = fission_package.pkg.name
|
||||
entrypoint = "handler"
|
||||
}
|
||||
|
||||
resource "fission_http_trigger" "route" {
|
||||
name = "tf-go-hello-route"
|
||||
function = fission_function.fn.name
|
||||
url = "/go-hello"
|
||||
methods = ["GET"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
sub {
|
||||
return "Hello from Perl in Fission";
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
fission = {
|
||||
source = "nail/fission"
|
||||
version = "~> 0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "fission" {
|
||||
kubeconfig_path = "/home/naeel/.kube/config"
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
# version = 1: Perl env uses v1 specialization (/specialize endpoint)
|
||||
resource "fission_environment" "perl" {
|
||||
name = "tf-perl-hello-env"
|
||||
image = "ghcr.io/fission/perl-env"
|
||||
version = 1
|
||||
}
|
||||
|
||||
resource "fission_package" "pkg" {
|
||||
name = "tf-perl-hello-pkg"
|
||||
environment = fission_environment.perl.name
|
||||
source_dir = "${path.module}/code"
|
||||
}
|
||||
|
||||
resource "fission_function" "fn" {
|
||||
name = "tf-perl-hello-fn"
|
||||
environment = fission_environment.perl.name
|
||||
package_name = fission_package.pkg.name
|
||||
entrypoint = "handler"
|
||||
}
|
||||
|
||||
resource "fission_http_trigger" "route" {
|
||||
name = "tf-perl-hello-route"
|
||||
function = fission_function.fn.name
|
||||
url = "/perl-hello"
|
||||
methods = ["GET"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
function handler($context)
|
||||
{
|
||||
/** @var \Psr\Http\Message\ResponseInterface $response */
|
||||
$response = $context["response"];
|
||||
$response->getBody()->write("Hello from PHP in Fission");
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
fission = {
|
||||
source = "nail/fission"
|
||||
version = "~> 0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "fission" {
|
||||
kubeconfig_path = "/home/naeel/.kube/config"
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
resource "fission_environment" "php" {
|
||||
name = "tf-php-hello-env"
|
||||
image = "ghcr.io/fission/php-env"
|
||||
version = 3
|
||||
}
|
||||
|
||||
resource "fission_package" "pkg" {
|
||||
name = "tf-php-hello-pkg"
|
||||
environment = fission_environment.php.name
|
||||
source_dir = "${path.module}/code"
|
||||
}
|
||||
|
||||
resource "fission_function" "fn" {
|
||||
name = "tf-php-hello-fn"
|
||||
environment = fission_environment.php.name
|
||||
package_name = fission_package.pkg.name
|
||||
entrypoint = "main.php::handler"
|
||||
}
|
||||
|
||||
resource "fission_http_trigger" "route" {
|
||||
name = "tf-php-hello-route"
|
||||
function = fission_function.fn.name
|
||||
url = "/php-hello"
|
||||
methods = ["GET"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
def handler
|
||||
"Hello from Ruby in Fission"
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
fission = {
|
||||
source = "nail/fission"
|
||||
version = "~> 0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "fission" {
|
||||
kubeconfig_path = "/home/naeel/.kube/config"
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
resource "fission_environment" "ruby" {
|
||||
name = "tf-ruby-hello-env"
|
||||
image = "ghcr.io/fission/ruby-env"
|
||||
version = 3
|
||||
}
|
||||
|
||||
resource "fission_package" "pkg" {
|
||||
name = "tf-ruby-hello-pkg"
|
||||
environment = fission_environment.ruby.name
|
||||
source_dir = "${path.module}/code"
|
||||
}
|
||||
|
||||
resource "fission_function" "fn" {
|
||||
name = "tf-ruby-hello-fn"
|
||||
environment = fission_environment.ruby.name
|
||||
package_name = fission_package.pkg.name
|
||||
entrypoint = "handler"
|
||||
}
|
||||
|
||||
resource "fission_http_trigger" "route" {
|
||||
name = "tf-ruby-hello-route"
|
||||
function = fission_function.fn.name
|
||||
url = "/ruby-hello"
|
||||
methods = ["GET"]
|
||||
}
|
||||
@@ -290,9 +290,16 @@ func unstructuredToFunctionModel(functionObject *unstructured.Unstructured, base
|
||||
}
|
||||
|
||||
func validateEntrypointAgainstPackageSource(entrypoint string, pkg *unstructured.Unstructured) error {
|
||||
if entrypoint == "" {
|
||||
return fmt.Errorf("entrypoint не может быть пустым")
|
||||
}
|
||||
|
||||
// Для Python/Go/JS: валидируем формат module.function и наличие функции в исходнике.
|
||||
// Для PHP (module::function), Ruby (function), Perl (function) — допускаем любой непустой формат.
|
||||
parts := strings.Split(entrypoint, ".")
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return fmt.Errorf("entrypoint %q должен иметь формат module.function", entrypoint)
|
||||
// Не стандартный module.function — допускаем (PHP, Ruby, Perl и др.)
|
||||
return nil
|
||||
}
|
||||
|
||||
if parts[0] != "main" {
|
||||
|
||||
@@ -70,7 +70,12 @@ func TestValidateEntrypointAgainstPackageSourcePythonMissing(t *testing.T) {
|
||||
|
||||
func TestValidateEntrypointAgainstPackageSourceBadFormat(t *testing.T) {
|
||||
pkg := &unstructured.Unstructured{}
|
||||
if err := validateEntrypointAgainstPackageSource("main", pkg); err == nil {
|
||||
t.Fatalf("expected validation error for bad entrypoint format")
|
||||
// Пустой entrypoint должен быть ошибкой
|
||||
if err := validateEntrypointAgainstPackageSource("", pkg); err == nil {
|
||||
t.Fatalf("expected validation error for empty entrypoint")
|
||||
}
|
||||
// Одиночное слово допустимо (Ruby, Perl)
|
||||
if err := validateEntrypointAgainstPackageSource("handler", pkg); err != nil {
|
||||
t.Fatalf("unexpected error for single-word entrypoint: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (r *PackageResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
||||
},
|
||||
"source_dir": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Путь к директории с кодом (поддерживаются main.py, main.js, main.go).",
|
||||
Description: "Путь к директории с кодом (поддерживаются main.py, main.js, main.go, main.php, main.rb, main.pl).",
|
||||
},
|
||||
"code_path": schema.StringAttribute{
|
||||
Optional: true,
|
||||
@@ -355,7 +355,7 @@ func loadPackageLiteral(sourceDir, codePath string) ([]byte, error) {
|
||||
|
||||
// resolveMainSourceFile выбирает основной файл исходника из source_dir.
|
||||
func resolveMainSourceFile(sourceDir string) (string, error) {
|
||||
candidates := []string{"main.py", "main.js", "main.go"}
|
||||
candidates := []string{"main.py", "main.js", "main.go", "main.php", "main.rb", "main.pl"}
|
||||
for _, candidate := range candidates {
|
||||
candidatePath := filepath.Join(sourceDir, candidate)
|
||||
fileInfo, err := os.Stat(candidatePath)
|
||||
@@ -364,7 +364,7 @@ func resolveMainSourceFile(sourceDir string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("source_dir %q must contain one of: main.py, main.js, main.go", sourceDir)
|
||||
return "", fmt.Errorf("source_dir %q must contain one of: main.py, main.js, main.go, main.php, main.rb, main.pl", sourceDir)
|
||||
}
|
||||
|
||||
// packageToUnstructured преобразует Terraform model в Kubernetes CRD payload.
|
||||
|
||||
Reference in New Issue
Block a user