- s3/client.go: Bucket() accessor + UploadContext() для tar.gz build context
- handler/upload.go: принимает zip, генерирует Dockerfile (FROM naeel/sless-runtime-{runtime}),
перепаковывает в tar.gz, загружает в S3, обновляет Function CRD → kaniko запускается
- router.go: маршрут POST .../upload зарегистрирован
102 lines
3.8 KiB
Go
102 lines
3.8 KiB
Go
// Изменено: 2026-03-07
|
|
// S3 storage — загрузка и скачивание zip архивов с кодом функций.
|
|
// Используем Ceph S3 compatible API (minio-go клиент умеет работать с любым S3).
|
|
// Код загружается пользователем через REST API, хранится в S3,
|
|
// builder скачивает его для сборки Docker образа.
|
|
|
|
package s3
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
)
|
|
|
|
// Client — клиент для работы с S3.
|
|
type Client struct {
|
|
mc *minio.Client
|
|
bucket string
|
|
}
|
|
|
|
// New создаёт клиент S3 и проверяет/создаёт бакет.
|
|
func New(endpoint, accessKey, secretKey, bucket string, useSSL bool) (*Client, error) {
|
|
mc, err := minio.New(endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
|
Secure: useSSL,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create minio client: %w", err)
|
|
}
|
|
return &Client{mc: mc, bucket: bucket}, nil
|
|
}
|
|
|
|
// EnsureBucket создаёт бакет если он не существует.
|
|
// Вызывается при старте сервиса.
|
|
func (c *Client) EnsureBucket(ctx context.Context) error {
|
|
exists, err := c.mc.BucketExists(ctx, c.bucket)
|
|
if err != nil {
|
|
return fmt.Errorf("check bucket: %w", err)
|
|
}
|
|
if !exists {
|
|
if err := c.mc.MakeBucket(ctx, c.bucket, minio.MakeBucketOptions{}); err != nil {
|
|
return fmt.Errorf("create bucket: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Upload загружает zip архив с кодом функции в S3.
|
|
// Ключ: functions/{namespace}/{name}/{version}.zip
|
|
// Возвращает ключ объекта для сохранения в CRD.
|
|
func (c *Client) Upload(ctx context.Context, namespace, funcName, version string, r io.Reader, size int64) (string, error) {
|
|
key := fmt.Sprintf("functions/%s/%s/%s.zip", namespace, funcName, version)
|
|
_, err := c.mc.PutObject(ctx, c.bucket, key, r, size, minio.PutObjectOptions{
|
|
ContentType: "application/zip",
|
|
})
|
|
if err != nil {
|
|
return "", fmt.Errorf("upload function code: %w", err)
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
// Download скачивает zip архив с кодом функции из S3.
|
|
// Используется builder'ом для сборки образа.
|
|
func (c *Client) Download(ctx context.Context, key string) (io.ReadCloser, error) {
|
|
obj, err := c.mc.GetObject(ctx, c.bucket, key, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("download function code: %w", err)
|
|
}
|
|
return obj, nil
|
|
}
|
|
|
|
// Delete удаляет архив с кодом функции из S3.
|
|
// Вызывается при удалении Function CRD.
|
|
func (c *Client) Delete(ctx context.Context, key string) error {
|
|
if err := c.mc.RemoveObject(ctx, c.bucket, key, minio.RemoveObjectOptions{}); err != nil {
|
|
return fmt.Errorf("delete function code: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Bucket возвращает имя бакета, с которым работает клиент.
|
|
func (c *Client) Bucket() string {
|
|
return c.bucket
|
|
}
|
|
|
|
// UploadContext загружает tar.gz с контекстом сборки (Dockerfile + код) в S3.
|
|
// Ключ: contexts/{namespace}/{name}/{version}.tar.gz
|
|
// Kaniko читает этот tar.gz как build context (--context=s3://bucket/key).
|
|
func (c *Client) UploadContext(ctx context.Context, namespace, funcName, version string, r io.Reader, size int64) (string, error) {
|
|
key := fmt.Sprintf("contexts/%s/%s/%s.tar.gz", namespace, funcName, version)
|
|
_, err := c.mc.PutObject(ctx, c.bucket, key, r, size, minio.PutObjectOptions{
|
|
ContentType: "application/gzip",
|
|
})
|
|
if err != nil {
|
|
return "", fmt.Errorf("upload build context: %w", err)
|
|
}
|
|
return key, nil
|
|
}
|