diff --git a/jobs/instances-snapshot/Dockerfile b/jobs/instances-snapshot/Dockerfile index b24a077..0ce8ee4 100644 --- a/jobs/instances-snapshot/Dockerfile +++ b/jobs/instances-snapshot/Dockerfile @@ -1,16 +1,27 @@ -# Using apline/golang image -FROM golang:1.24-alpine +# Build stage +FROM golang:1.25-alpine AS builder -# Set destination for COPY WORKDIR /app # Copy required files -COPY go.mod ./ -COPY go.sum ./ +COPY go.mod go.sum ./ +RUN go mod download + COPY *.go ./ # Build the executable -RUN go build -o /jobs-snapshot +RUN CGO_ENABLED=0 GOOS=linux go build -o /jobs-snapshot + +# Final stage +FROM alpine:latest + +WORKDIR /app + +# Install CA certificates for HTTPS +RUN apk --no-cache add ca-certificates + +# Copy the binary from the builder stage +COPY --from=builder /jobs-snapshot /app/jobs-snapshot # Run the executable -ENTRYPOINT [ "/jobs-snapshot" ] +ENTRYPOINT [ "/app/jobs-snapshot" ] diff --git a/jobs/instances-snapshot/go.mod b/jobs/instances-snapshot/go.mod index a00eab1..66426ff 100644 --- a/jobs/instances-snapshot/go.mod +++ b/jobs/instances-snapshot/go.mod @@ -1,8 +1,8 @@ module github.com/scaleway/serverless-examples/jobs/instances-snapshot -go 1.24 +go 1.25 -require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 +require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 require ( github.com/kr/pretty v0.3.1 // indirect diff --git a/jobs/instances-snapshot/go.sum b/jobs/instances-snapshot/go.sum index 43e00dc..cad1bab 100644 --- a/jobs/instances-snapshot/go.sum +++ b/jobs/instances-snapshot/go.sum @@ -1,6 +1,4 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -12,10 +10,16 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 h1:KhF0WejiUTDbL5X55nXowP7zNopwpowa6qaMAWyIE+0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33/go.mod h1:792k1RTU+5JeMXm35/e2Wgp71qPH/DmDoZrRc+EFZDk= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36/go.mod h1:LEsDu4BubxK7/cWhtlQWfuxwL4rf/2UEpxXz1o1EMtM= +go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= +go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/dnaeon/go-vcr.v4 v4.0.6 h1:PiJkrakkmzc5s7EfBnZOnyiLwi7o7A9fwPzN0X2uwe0= +gopkg.in/dnaeon/go-vcr.v4 v4.0.6/go.mod h1:sbq5oMEcM4PXngbcNbHhzfCP9OdZodLhrbRYoyg09HY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/jobs/instances-snapshot/main.go b/jobs/instances-snapshot/main.go index b3d5c2a..4c9e589 100644 --- a/jobs/instances-snapshot/main.go +++ b/jobs/instances-snapshot/main.go @@ -1,7 +1,9 @@ package main import ( + "context" "fmt" + "log/slog" "os" "github.com/scaleway/scaleway-sdk-go/api/block/v1" @@ -19,7 +21,21 @@ const ( ) func main() { - fmt.Println("creating snapshot of instance...") + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + slog.SetDefault(logger) + + if err := run(); err != nil { + slog.Error("application failed", "error", err) + os.Exit(1) + } +} + +func run() error { + if err := checkEnvVars(); err != nil { + return err + } + + slog.Info("creating snapshot of instance...") // Create a Scaleway client with credentials from environment variables. client, err := scw.NewClient( @@ -32,52 +48,62 @@ func main() { // Get more about our availability // zones at https://www.scaleway.com/en/docs/console/my-account/reference-content/products-availability/ scw.WithDefaultRegion(scw.RegionFrPar), + + scw.WithDefaultZone(scw.Zone(os.Getenv(envInstanceZone))), ) if err != nil { - panic(err) + return fmt.Errorf("failed to create client: %w", err) } // Create SDK objects for Scaleway Instance product instanceAPI := instance.NewAPI(client) - blockAPI := block.NewAPI(client) - if err := createSnapshots(instanceAPI, blockAPI); err != nil { - panic(err) + ctx := context.Background() + + if err := createSnapshots(ctx, instanceAPI, blockAPI); err != nil { + return err } + + return nil } -func createSnapshots(instanceAPI *instance.API, blockAPI *block.API) error { +func createSnapshots(ctx context.Context, instanceAPI *instance.API, blockAPI *block.API) error { + zone := scw.Zone(os.Getenv(envInstanceZone)) + instanceID := os.Getenv(envInstanceID) + gotInstance, err := instanceAPI.GetServer(&instance.GetServerRequest{ - ServerID: os.Getenv(envInstanceID), - Zone: scw.Zone(os.Getenv(envInstanceZone)), - }) + ServerID: instanceID, + Zone: zone, + }, scw.WithContext(ctx)) if err != nil { - return fmt.Errorf("error while getting instance %w", err) + return fmt.Errorf("error while getting instance: %w", err) } for _, volume := range gotInstance.Server.Volumes { + slog.Info("creating snapshot for volume", "volume_id", volume.ID) snapshotResp, err := blockAPI.CreateSnapshot(&block.CreateSnapshotRequest{ - Zone: scw.Zone(os.Getenv(envInstanceZone)), + Zone: zone, VolumeID: volume.ID, ProjectID: os.Getenv(envProjectID), - }) + }, scw.WithContext(ctx)) if err != nil { - return fmt.Errorf("error while creating snapshot %w", err) + return fmt.Errorf("error while creating snapshot: %w", err) } - fmt.Println("created snapshot ", snapshotResp.ID) + slog.Info("created snapshot", "snapshot_id", snapshotResp.ID) } return nil } -func init() { - mandatoryVariables := [...]string{envOrgID, envAccessKey, envSecretKey, envInstanceID, envInstanceZone} +func checkEnvVars() error { + mandatoryVariables := []string{envOrgID, envAccessKey, envSecretKey, envInstanceID, envInstanceZone, envProjectID} - for idx := range mandatoryVariables { - if os.Getenv(mandatoryVariables[idx]) == "" { - panic("missing environment variable " + mandatoryVariables[idx]) + for _, v := range mandatoryVariables { + if os.Getenv(v) == "" { + return fmt.Errorf("missing environment variable: %s", v) } } + return nil }