mirror of
https://github.com/apricote/hcloud-upload-image.git
synced 2026-01-13 21:31:03 +00:00
Compare commits
52 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7a29b2bee | ||
| b440f8d7f6 | |||
| 8f7f0643b5 | |||
| 99691d54ab | |||
| d7ff673261 | |||
|
|
740dbb5c68 | ||
|
|
919461ddd0 | ||
|
|
0280f677e0 | ||
|
|
c4c86073ae | ||
|
|
f59aa2bb23 | ||
|
|
f78a5aa191 | ||
|
|
a73c1e4758 | ||
|
|
bafcd0c57b | ||
|
|
6a41192364 | ||
|
|
e9c4d29792 | ||
|
|
fcbc14aab6 | ||
| a9b16cf07c | |||
| d87db70674 | |||
| 5bbd840ea7 | |||
|
|
92e0397f7c | ||
| 5eba2d52fe | |||
| a20561944d | |||
|
|
43d0405d2b | ||
|
|
27916d2c94 | ||
|
|
04bfe9bcfa | ||
|
|
7537226258 | ||
|
|
37ebbce517 | ||
|
|
921d688fd4 | ||
|
|
fbd639ba59 | ||
|
|
b877383556 | ||
|
|
b5abcc149a | ||
|
|
892b806470 | ||
|
|
b64a100eff | ||
|
|
4167a5c27b | ||
|
|
3c0dd7bf51 | ||
|
|
cbcfd7d007 | ||
|
|
b2c2c8e979 | ||
|
|
bcf19efc64 | ||
|
|
8a03eb9e9b | ||
| b9af8855d5 | |||
| 6f949a2ba7 | |||
| 03f08da8a3 | |||
| fdfb284533 | |||
| 420dcf94c9 | |||
| 28bf5380f3 | |||
| 4ff1883fd0 | |||
| 7f10f7b253 | |||
| e490b9a7f3 | |||
|
|
b093e1eda8 | ||
|
|
f12b6b076b | ||
|
|
7cffed4ba7 | ||
|
|
19f1e085e9 |
24 changed files with 472 additions and 200 deletions
2
.github/release-please-manifest.json
vendored
2
.github/release-please-manifest.json
vendored
|
|
@ -1 +1 @@
|
||||||
{".":"1.0.0","hcloudimages":"1.0.0"}
|
{".":"1.3.0","hcloudimages":"1.3.0"}
|
||||||
|
|
|
||||||
24
.github/workflows/ci.yaml
vendored
24
.github/workflows/ci.yaml
vendored
|
|
@ -10,23 +10,23 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
- name: Run golangci-lint (CLI)
|
- name: Run golangci-lint (CLI)
|
||||||
uses: golangci/golangci-lint-action@v7
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: v2.1.5 # renovate: datasource=github-releases depName=golangci/golangci-lint
|
version: v2.7.2 # renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||||
args: --timeout 5m
|
args: --timeout 5m
|
||||||
|
|
||||||
- name: Run golangci-lint (Lib)
|
- name: Run golangci-lint (Lib)
|
||||||
uses: golangci/golangci-lint-action@v7
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: v2.1.5 # renovate: datasource=github-releases depName=golangci/golangci-lint
|
version: v2.7.2 # renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||||
args: --timeout 5m
|
args: --timeout 5m
|
||||||
working-directory: hcloudimages
|
working-directory: hcloudimages
|
||||||
|
|
||||||
|
|
@ -34,10 +34,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|
@ -48,10 +48,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|
@ -68,10 +68,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|
|
||||||
6
.github/workflows/docs.yaml
vendored
6
.github/workflows/docs.yaml
vendored
|
|
@ -13,13 +13,13 @@ jobs:
|
||||||
id-token: write # To update the deployment status
|
id-token: write # To update the deployment status
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
with:
|
with:
|
||||||
lfs: "true"
|
lfs: "true"
|
||||||
|
|
||||||
- uses: ./.github/actions/setup-mdbook
|
- uses: ./.github/actions/setup-mdbook
|
||||||
with:
|
with:
|
||||||
version: v0.4.48 # renovate: datasource=github-releases depName=rust-lang/mdbook
|
version: v0.5.2 # renovate: datasource=github-releases depName=rust-lang/mdbook
|
||||||
|
|
||||||
- name: Build Book
|
- name: Build Book
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
|
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
|
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
|
||||||
with:
|
with:
|
||||||
# Upload entire repository
|
# Upload entire repository
|
||||||
path: "docs/book"
|
path: "docs/book"
|
||||||
|
|
|
||||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
|
|
@ -14,17 +14,17 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
|
uses: docker/login-action@6862ffc5ab2cdb4405cf318a62a6f4c066e2298b
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|
|
||||||
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -1,5 +1,40 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.3.0](https://github.com/apricote/hcloud-upload-image/compare/v1.2.0...v1.3.0) (2025-12-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add --location flag to specify datacenter region ([#141](https://github.com/apricote/hcloud-upload-image/issues/141)) ([fcbc14a](https://github.com/apricote/hcloud-upload-image/commit/fcbc14aab6d495d2c67d653f9ea1ff56a39a8c2f)), closes [#142](https://github.com/apricote/hcloud-upload-image/issues/142)
|
||||||
|
|
||||||
|
## [1.2.0](https://github.com/apricote/hcloud-upload-image/compare/v1.1.0...v1.2.0) (2025-11-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* change minimum required Go version to 1.24 ([#130](https://github.com/apricote/hcloud-upload-image/issues/130)) ([5eba2d5](https://github.com/apricote/hcloud-upload-image/commit/5eba2d52fe3aafb4fd0d93403548f4c32bc2b5ac))
|
||||||
|
* support zstd compression ([#125](https://github.com/apricote/hcloud-upload-image/issues/125)) ([37ebbce](https://github.com/apricote/hcloud-upload-image/commit/37ebbce5179997ac216af274055fc34c777b01e6)), closes [#122](https://github.com/apricote/hcloud-upload-image/issues/122)
|
||||||
|
* update default x86 server type to cx23 ([#129](https://github.com/apricote/hcloud-upload-image/issues/129)) ([a205619](https://github.com/apricote/hcloud-upload-image/commit/a20561944d0ba9485a6e10e99df15c56a688541d))
|
||||||
|
|
||||||
|
## [1.1.0](https://github.com/apricote/hcloud-upload-image/compare/v1.0.1...v1.1.0) (2025-05-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* smaller snapshots by zeroing disk first ([#101](https://github.com/apricote/hcloud-upload-image/issues/101)) ([fdfb284](https://github.com/apricote/hcloud-upload-image/commit/fdfb284533d3154806b0936c08015fd5cc64b0fb)), closes [#96](https://github.com/apricote/hcloud-upload-image/issues/96)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* upload from local image generates broken command ([#98](https://github.com/apricote/hcloud-upload-image/issues/98)) ([420dcf9](https://github.com/apricote/hcloud-upload-image/commit/420dcf94c965ee470602db6c9c23c777fda91222)), closes [#97](https://github.com/apricote/hcloud-upload-image/issues/97)
|
||||||
|
|
||||||
|
## [1.0.1](https://github.com/apricote/hcloud-upload-image/compare/v1.0.0...v1.0.1) (2025-05-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* timeout while waiting for SSH to become available ([#92](https://github.com/apricote/hcloud-upload-image/issues/92)) ([e490b9a](https://github.com/apricote/hcloud-upload-image/commit/e490b9a7f394e268fa1946ca51aa998c78c3d46a))
|
||||||
|
|
||||||
## [1.0.0](https://github.com/apricote/hcloud-upload-image/compare/v0.3.1...v1.0.0) (2025-05-04)
|
## [1.0.0](https://github.com/apricote/hcloud-upload-image/compare/v0.3.1...v1.0.0) (2025-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
28
README.md
28
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
# `hcloud-upload-image`
|
# hcloud-upload-image
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Quickly upload any raw disk images into your <a href="https://hetzner.com/cloud" target="_blank">Hetzner Cloud</a> projects!
|
Quickly upload any raw disk images into your <a href="https://hetzner.com/cloud" target="_blank">Hetzner Cloud</a> projects!
|
||||||
|
|
@ -13,26 +13,22 @@
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
The [Hetzner Cloud API](https://docs.hetzner.cloud/) does not support uploading disk images directly, and it only
|
The [Hetzner Cloud API](https://docs.hetzner.cloud/) does not support uploading disk images directly and only provides a limited set of default images. The only option for custom disk images is to take a snapshot of an existing server’s root disk. These snapshots can then be used to create new servers.
|
||||||
provides a limited set of default images. The only option for custom disk images that users have is by taking a
|
|
||||||
"snapshot" of an existing servers root disk. These can then be used to create new servers.
|
|
||||||
|
|
||||||
To create a completely custom disk image, users have to follow these steps:
|
To create a completely custom disk image, users need to follow these steps:
|
||||||
|
|
||||||
1. Create server with the correct server type
|
1. Create a server with the correct server type
|
||||||
2. Enable rescue system for the server
|
2. Enable the rescue system for the server
|
||||||
3. Boot the server
|
3. Boot the server
|
||||||
4. Download the disk image from within the rescue system
|
4. Download the disk image from within the rescue system
|
||||||
5. Write disk image to servers root disk
|
5. Write the disk image to the server’s root disk
|
||||||
6. Shut down the server
|
6. Shut down the server
|
||||||
7. Take a snapshot of the servers root disk
|
7. Take a snapshot of the server’s root disk
|
||||||
8. Delete the server
|
8. Delete the server
|
||||||
|
|
||||||
This is an annoyingly long process. Many users have automated this with [Packer](https://www.packer.io/) &
|
This is a frustratingly long process. Many users have automated it with [Packer](https://www.packer.io/) and [`packer-plugin-hcloud`](https://github.com/hetznercloud/packer-plugin-hcloud/), but Packer introduces additional complexity that can be difficult to manage.
|
||||||
[`packer-plugin-hcloud`](https://github.com/hetznercloud/packer-plugin-hcloud/) before, but Packer offers a lot of
|
|
||||||
additional complexity to wrap your head around.
|
|
||||||
|
|
||||||
This repository provides a simple CLI tool & Go library to do the above.
|
This repository provides a simple CLI tool and Go library to streamline the process.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|
@ -44,7 +40,7 @@ We provide pre-built `deb`, `rpm` and `apk` packages. Alternatively we also prov
|
||||||
|
|
||||||
Check out the [GitHub release artifacts](https://github.com/apricote/hcloud-upload-image/releases/latest) for all of these files and archives.
|
Check out the [GitHub release artifacts](https://github.com/apricote/hcloud-upload-image/releases/latest) for all of these files and archives.
|
||||||
|
|
||||||
##### Arch Linux
|
#### Arch Linux
|
||||||
|
|
||||||
You can get [`hcloud-upload-image-bin`](https://aur.archlinux.org/packages/hcloud-upload-image-bin) from the AUR.
|
You can get [`hcloud-upload-image-bin`](https://aur.archlinux.org/packages/hcloud-upload-image-bin) from the AUR.
|
||||||
|
|
||||||
|
|
@ -77,7 +73,8 @@ export HCLOUD_TOKEN="<your token>"
|
||||||
hcloud-upload-image upload \
|
hcloud-upload-image upload \
|
||||||
--image-url "https://example.com/disk-image-x86.raw.bz2" \
|
--image-url "https://example.com/disk-image-x86.raw.bz2" \
|
||||||
--architecture x86 \
|
--architecture x86 \
|
||||||
--compression bz2
|
--compression bz2 \
|
||||||
|
--location nbg1 # Optional: defaults to fsn1
|
||||||
```
|
```
|
||||||
|
|
||||||
To learn more, you can use the embedded help output or check out the [CLI help pages in this repository](docs/reference/cli/hcloud-upload-image.md).:
|
To learn more, you can use the embedded help output or check out the [CLI help pages in this repository](docs/reference/cli/hcloud-upload-image.md).:
|
||||||
|
|
@ -127,6 +124,7 @@ func main() {
|
||||||
ImageURL: imageURL,
|
ImageURL: imageURL,
|
||||||
ImageCompression: hcloudimages.CompressionBZ2,
|
ImageCompression: hcloudimages.CompressionBZ2,
|
||||||
Architecture: hcloud.ArchitectureX86,
|
Architecture: hcloud.ArchitectureX86,
|
||||||
|
Location: &hcloud.Location{Name: "nbg1"}, // Optional: defaults to fsn1
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages"
|
"github.com/apricote/hcloud-upload-image/hcloudimages"
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages/backoff"
|
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
|
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
|
||||||
"github.com/apricote/hcloud-upload-image/internal/ui"
|
"github.com/apricote/hcloud-upload-image/internal/ui"
|
||||||
"github.com/apricote/hcloud-upload-image/internal/version"
|
"github.com/apricote/hcloud-upload-image/internal/version"
|
||||||
|
|
@ -89,7 +88,7 @@ func initClient(cmd *cobra.Command, _ []string) {
|
||||||
opts := []hcloud.ClientOption{
|
opts := []hcloud.ClientOption{
|
||||||
hcloud.WithToken(os.Getenv("HCLOUD_TOKEN")),
|
hcloud.WithToken(os.Getenv("HCLOUD_TOKEN")),
|
||||||
hcloud.WithApplication("hcloud-upload-image", version.Version),
|
hcloud.WithApplication("hcloud-upload-image", version.Version),
|
||||||
hcloud.WithPollOpts(hcloud.PollOpts{BackoffFunc: backoff.ExponentialBackoffWithLimit(2, 1*time.Second, 30*time.Second)}),
|
hcloud.WithPollOpts(hcloud.PollOpts{BackoffFunc: hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{Multiplier: 2, Base: 1 * time.Second, Cap: 30 * time.Second})}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("HCLOUD_DEBUG") != "" || verbose >= 2 {
|
if os.Getenv("HCLOUD_DEBUG") != "" || verbose >= 2 {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ const (
|
||||||
uploadFlagServerType = "server-type"
|
uploadFlagServerType = "server-type"
|
||||||
uploadFlagDescription = "description"
|
uploadFlagDescription = "description"
|
||||||
uploadFlagLabels = "labels"
|
uploadFlagLabels = "labels"
|
||||||
|
uploadFlagLocation = "location"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed upload.md
|
//go:embed upload.md
|
||||||
|
|
@ -54,6 +55,7 @@ var uploadCmd = &cobra.Command{
|
||||||
serverType, _ := cmd.Flags().GetString(uploadFlagServerType)
|
serverType, _ := cmd.Flags().GetString(uploadFlagServerType)
|
||||||
description, _ := cmd.Flags().GetString(uploadFlagDescription)
|
description, _ := cmd.Flags().GetString(uploadFlagDescription)
|
||||||
labels, _ := cmd.Flags().GetStringToString(uploadFlagLabels)
|
labels, _ := cmd.Flags().GetStringToString(uploadFlagLabels)
|
||||||
|
location, _ := cmd.Flags().GetString(uploadFlagLocation)
|
||||||
|
|
||||||
options := hcloudimages.UploadOptions{
|
options := hcloudimages.UploadOptions{
|
||||||
ImageCompression: hcloudimages.Compression(imageCompression),
|
ImageCompression: hcloudimages.Compression(imageCompression),
|
||||||
|
|
@ -102,6 +104,10 @@ var uploadCmd = &cobra.Command{
|
||||||
options.ServerType = &hcloud.ServerType{Name: serverType}
|
options.ServerType = &hcloud.ServerType{Name: serverType}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if location != "" {
|
||||||
|
options.Location = &hcloud.Location{Name: location}
|
||||||
|
}
|
||||||
|
|
||||||
image, err := client.Upload(ctx, options)
|
image, err := client.Upload(ctx, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to upload the image: %w", err)
|
return fmt.Errorf("failed to upload the image: %w", err)
|
||||||
|
|
@ -121,13 +127,13 @@ func init() {
|
||||||
uploadCmd.MarkFlagsMutuallyExclusive(uploadFlagImageURL, uploadFlagImagePath)
|
uploadCmd.MarkFlagsMutuallyExclusive(uploadFlagImageURL, uploadFlagImagePath)
|
||||||
uploadCmd.MarkFlagsOneRequired(uploadFlagImageURL, uploadFlagImagePath)
|
uploadCmd.MarkFlagsOneRequired(uploadFlagImageURL, uploadFlagImagePath)
|
||||||
|
|
||||||
uploadCmd.Flags().String(uploadFlagCompression, "", "Type of compression that was used on the disk image [choices: bz2, xz]")
|
uploadCmd.Flags().String(uploadFlagCompression, "", "Type of compression that was used on the disk image [choices: bz2, xz, zstd]")
|
||||||
_ = uploadCmd.RegisterFlagCompletionFunc(
|
_ = uploadCmd.RegisterFlagCompletionFunc(
|
||||||
uploadFlagCompression,
|
uploadFlagCompression,
|
||||||
cobra.FixedCompletions([]string{string(hcloudimages.CompressionBZ2), string(hcloudimages.CompressionXZ)}, cobra.ShellCompDirectiveNoFileComp),
|
cobra.FixedCompletions([]string{string(hcloudimages.CompressionBZ2), string(hcloudimages.CompressionXZ), string(hcloudimages.CompressionZSTD)}, cobra.ShellCompDirectiveNoFileComp),
|
||||||
)
|
)
|
||||||
|
|
||||||
uploadCmd.Flags().String(uploadFlagFormat, "", "Format of the image. [choices: qcow2]")
|
uploadCmd.Flags().String(uploadFlagFormat, "", "Format of the image. [default: raw, choices: qcow2]")
|
||||||
_ = uploadCmd.RegisterFlagCompletionFunc(
|
_ = uploadCmd.RegisterFlagCompletionFunc(
|
||||||
uploadFlagFormat,
|
uploadFlagFormat,
|
||||||
cobra.FixedCompletions([]string{string(hcloudimages.FormatQCOW2)}, cobra.ShellCompDirectiveNoFileComp),
|
cobra.FixedCompletions([]string{string(hcloudimages.FormatQCOW2)}, cobra.ShellCompDirectiveNoFileComp),
|
||||||
|
|
@ -148,4 +154,10 @@ func init() {
|
||||||
uploadCmd.Flags().String(uploadFlagDescription, "", "Description for the resulting image")
|
uploadCmd.Flags().String(uploadFlagDescription, "", "Description for the resulting image")
|
||||||
|
|
||||||
uploadCmd.Flags().StringToString(uploadFlagLabels, map[string]string{}, "Labels for the resulting image")
|
uploadCmd.Flags().StringToString(uploadFlagLabels, map[string]string{}, "Labels for the resulting image")
|
||||||
|
|
||||||
|
uploadCmd.Flags().String(uploadFlagLocation, "", "Datacenter location for the temporary server [default: fsn1, choices: fsn1, nbg1, hel1, ash, hil, sin]")
|
||||||
|
_ = uploadCmd.RegisterFlagCompletionFunc(
|
||||||
|
uploadFlagLocation,
|
||||||
|
cobra.FixedCompletions([]string{"fsn1", "nbg1", "hel1", "ash", "hil", "sin"}, cobra.ShellCompDirectiveNoFileComp),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
[book]
|
[book]
|
||||||
language = "en"
|
language = "en"
|
||||||
multilingual = false
|
|
||||||
src = "."
|
src = "."
|
||||||
title = "hcloud-upload-image"
|
title = "hcloud-upload-image"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,14 @@ hcloud-upload-image upload (--image-path=<local-path> | --image-url=<url>) --arc
|
||||||
|
|
||||||
```
|
```
|
||||||
--architecture string CPU architecture of the disk image [choices: x86, arm]
|
--architecture string CPU architecture of the disk image [choices: x86, arm]
|
||||||
--compression string Type of compression that was used on the disk image [choices: bz2, xz]
|
--compression string Type of compression that was used on the disk image [choices: bz2, xz, zstd]
|
||||||
--description string Description for the resulting image
|
--description string Description for the resulting image
|
||||||
--format string Format of the image. [choices: qcow2]
|
--format string Format of the image. [default: raw, choices: qcow2]
|
||||||
-h, --help help for upload
|
-h, --help help for upload
|
||||||
--image-path string Local path to the disk image that should be uploaded
|
--image-path string Local path to the disk image that should be uploaded
|
||||||
--image-url string Remote URL of the disk image that should be uploaded
|
--image-url string Remote URL of the disk image that should be uploaded
|
||||||
--labels stringToString Labels for the resulting image (default [])
|
--labels stringToString Labels for the resulting image (default [])
|
||||||
|
--location string Datacenter location for the temporary server [default: fsn1, choices: fsn1, nbg1, hel1, ash, hil, sin]
|
||||||
--server-type string Explicitly use this server type to generate the image. Mutually exclusive with --architecture.
|
--server-type string Explicitly use this server type to generate the image. Mutually exclusive with --architecture.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
34
go.mod
34
go.mod
|
|
@ -1,13 +1,13 @@
|
||||||
module github.com/apricote/hcloud-upload-image
|
module github.com/apricote/hcloud-upload-image
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.24.2
|
toolchain go1.25.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apricote/hcloud-upload-image/hcloudimages v1.0.0
|
github.com/apricote/hcloud-upload-image/hcloudimages v1.3.0
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.10.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
@ -15,18 +15,18 @@ require (
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/prometheus/client_golang v1.21.1 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.9 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.1 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
70
go.sum
70
go.sum
|
|
@ -1,5 +1,5 @@
|
||||||
github.com/apricote/hcloud-upload-image/hcloudimages v1.0.0 h1:Gq0SSuPCiZFApGQ3SIEEQqD8btD6tRwfOxr+cky7oTo=
|
github.com/apricote/hcloud-upload-image/hcloudimages v1.3.0 h1:FVIKGSqpxdkO4+t1N8a8xu/xxc+13vHy2QaTAWICuQo=
|
||||||
github.com/apricote/hcloud-upload-image/hcloudimages v1.0.0/go.mod h1:MwBmhSPlwS3S3ynOsFXPbco40Iv3udqhXKz2EXKSpVU=
|
github.com/apricote/hcloud-upload-image/hcloudimages v1.3.0/go.mod h1:NiCZ7xGoYNbWeK9L083leB7/g5oa7SAOZq405XkUSeQ=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
|
@ -10,12 +10,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0 h1:wUpQT+fgAxIcdMtFvuCJ78ziqc/VARubpOQPQyj4Q84=
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0 h1:mxasKipFPDPzni85xcMgwYci2PH8TalLgyPJoTlhWDA=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0/go.mod h1:WSM7w+9tT86sJTNcF8a/oHljC3HUmQfcLxYsgx6PpSc=
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0/go.mod h1:aF9x0XYNRRQ7N4gux/4cEJeGAHcggu7sX+BBA1rv8ks=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
|
@ -26,36 +26,42 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
|
||||||
4
go.work
4
go.work
|
|
@ -1,6 +1,6 @@
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.24.2
|
toolchain go1.25.4
|
||||||
|
|
||||||
use (
|
use (
|
||||||
.
|
.
|
||||||
|
|
|
||||||
16
go.work.sum
16
go.work.sum
|
|
@ -3,7 +3,7 @@ github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjH
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/apricote/hcloud-upload-image/hcloudimages v1.0.0/go.mod h1:MwBmhSPlwS3S3ynOsFXPbco40Iv3udqhXKz2EXKSpVU=
|
github.com/apricote/hcloud-upload-image/hcloudimages v1.1.0/go.mod h1:iJ95BaLfISZBY9X8K2Y2A5a49dI0RLjAuq+4BqlOSgA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
|
@ -37,12 +37,16 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/jessevdk/go-flags v1.4.1-0.20181029123624-5de817a9aa20 h1:dAOsPLhnBzIyxu0VvmnKjlNcIlgMK+erD6VRHDtweMI=
|
github.com/jessevdk/go-flags v1.4.1-0.20181029123624-5de817a9aa20 h1:dAOsPLhnBzIyxu0VvmnKjlNcIlgMK+erD6VRHDtweMI=
|
||||||
github.com/jessevdk/go-flags v1.4.1-0.20181029123624-5de817a9aa20/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.1-0.20181029123624-5de817a9aa20/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||||
|
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
|
||||||
github.com/jmattheis/goverter v1.4.0 h1:SrboBYMpGkj1XSgFhWwqzdP024zIa1+58YzUm+0jcBE=
|
github.com/jmattheis/goverter v1.4.0 h1:SrboBYMpGkj1XSgFhWwqzdP024zIa1+58YzUm+0jcBE=
|
||||||
github.com/jmattheis/goverter v1.4.0/go.mod h1:iVIl/4qItWjWj2g3vjouGoYensJbRqDHpzlEVMHHFeY=
|
github.com/jmattheis/goverter v1.4.0/go.mod h1:iVIl/4qItWjWj2g3vjouGoYensJbRqDHpzlEVMHHFeY=
|
||||||
github.com/jmattheis/goverter v1.5.1 h1:NdBYrF1V1EFQbAA1M/ZR4YVbQjxVl3L6Xupn7moF3LU=
|
github.com/jmattheis/goverter v1.5.1 h1:NdBYrF1V1EFQbAA1M/ZR4YVbQjxVl3L6Xupn7moF3LU=
|
||||||
github.com/jmattheis/goverter v1.5.1/go.mod h1:iVIl/4qItWjWj2g3vjouGoYensJbRqDHpzlEVMHHFeY=
|
github.com/jmattheis/goverter v1.5.1/go.mod h1:iVIl/4qItWjWj2g3vjouGoYensJbRqDHpzlEVMHHFeY=
|
||||||
github.com/jmattheis/goverter v1.8.0 h1:P8GQ/uJEzCwpNdm5vKxaAjDDMxTpsAJZxgrXegicAW8=
|
github.com/jmattheis/goverter v1.8.0 h1:P8GQ/uJEzCwpNdm5vKxaAjDDMxTpsAJZxgrXegicAW8=
|
||||||
github.com/jmattheis/goverter v1.8.0/go.mod h1:c8TVzpum2NThy2eJ/Wz3tyqRxzpElP2xDfoHOIDrNSQ=
|
github.com/jmattheis/goverter v1.8.0/go.mod h1:c8TVzpum2NThy2eJ/Wz3tyqRxzpElP2xDfoHOIDrNSQ=
|
||||||
|
github.com/jmattheis/goverter v1.9.2 h1:pBjvkhJ0F3PKMqGyHPL0yqnbTe08jjZqt/Z9ZmNKtTQ=
|
||||||
|
github.com/jmattheis/goverter v1.9.2/go.mod h1:1n3q6zf7j58tXcRWHbLFxK2Jk8WQVzr0d3nuaCcRqeg=
|
||||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
|
@ -69,6 +73,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/vburenin/ifacemaker v1.2.1 h1:3Vq8B/bfBgjWTkv+jDg4dVL1KHt3k1K4lO7XRxYA2sk=
|
github.com/vburenin/ifacemaker v1.2.1 h1:3Vq8B/bfBgjWTkv+jDg4dVL1KHt3k1K4lO7XRxYA2sk=
|
||||||
github.com/vburenin/ifacemaker v1.2.1/go.mod h1:5WqrzX2aD7/hi+okBjcaEQJMg4lDGrpuEX3B8L4Wgrs=
|
github.com/vburenin/ifacemaker v1.2.1/go.mod h1:5WqrzX2aD7/hi+okBjcaEQJMg4lDGrpuEX3B8L4Wgrs=
|
||||||
|
github.com/vburenin/ifacemaker v1.3.0 h1:X5//v/1tyORf5157wLATgP1wgquW3FUW91/OGHLRqGo=
|
||||||
|
github.com/vburenin/ifacemaker v1.3.0/go.mod h1:SxTD9m+6uBQyhd0aohV7R4iirO+l9mEoTn4nSe67vMs=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
|
|
@ -81,6 +87,8 @@ golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
|
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||||
|
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
|
|
@ -91,6 +99,8 @@ golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
|
|
@ -107,6 +117,8 @@ golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
|
@ -125,6 +137,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJ
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||||
|
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||||
|
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,40 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.3.0](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v1.2.0...hcloudimages/v1.3.0) (2025-12-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add --location flag to specify datacenter region ([#141](https://github.com/apricote/hcloud-upload-image/issues/141)) ([fcbc14a](https://github.com/apricote/hcloud-upload-image/commit/fcbc14aab6d495d2c67d653f9ea1ff56a39a8c2f)), closes [#142](https://github.com/apricote/hcloud-upload-image/issues/142)
|
||||||
|
|
||||||
|
## [1.2.0](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v1.1.0...hcloudimages/v1.2.0) (2025-11-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* change minimum required Go version to 1.24 ([#130](https://github.com/apricote/hcloud-upload-image/issues/130)) ([5eba2d5](https://github.com/apricote/hcloud-upload-image/commit/5eba2d52fe3aafb4fd0d93403548f4c32bc2b5ac))
|
||||||
|
* support zstd compression ([#125](https://github.com/apricote/hcloud-upload-image/issues/125)) ([37ebbce](https://github.com/apricote/hcloud-upload-image/commit/37ebbce5179997ac216af274055fc34c777b01e6)), closes [#122](https://github.com/apricote/hcloud-upload-image/issues/122)
|
||||||
|
* update default x86 server type to cx23 ([#129](https://github.com/apricote/hcloud-upload-image/issues/129)) ([a205619](https://github.com/apricote/hcloud-upload-image/commit/a20561944d0ba9485a6e10e99df15c56a688541d))
|
||||||
|
|
||||||
|
## [1.1.0](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v1.0.1...hcloudimages/v1.1.0) (2025-05-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* smaller snapshots by zeroing disk first ([#101](https://github.com/apricote/hcloud-upload-image/issues/101)) ([fdfb284](https://github.com/apricote/hcloud-upload-image/commit/fdfb284533d3154806b0936c08015fd5cc64b0fb)), closes [#96](https://github.com/apricote/hcloud-upload-image/issues/96)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* upload from local image generates broken command ([#98](https://github.com/apricote/hcloud-upload-image/issues/98)) ([420dcf9](https://github.com/apricote/hcloud-upload-image/commit/420dcf94c965ee470602db6c9c23c777fda91222)), closes [#97](https://github.com/apricote/hcloud-upload-image/issues/97)
|
||||||
|
|
||||||
|
## [1.0.1](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v1.0.0...hcloudimages/v1.0.1) (2025-05-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* timeout while waiting for SSH to become available ([#92](https://github.com/apricote/hcloud-upload-image/issues/92)) ([e490b9a](https://github.com/apricote/hcloud-upload-image/commit/e490b9a7f394e268fa1946ca51aa998c78c3d46a))
|
||||||
|
|
||||||
## [1.0.0](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v0.3.1...hcloudimages/v1.0.0) (2025-05-04)
|
## [1.0.0](https://github.com/apricote/hcloud-upload-image/compare/hcloudimages/v0.3.1...hcloudimages/v1.0.0) (2025-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ import (
|
||||||
// It uses the formula:
|
// It uses the formula:
|
||||||
//
|
//
|
||||||
// min(b^retries * d, limit)
|
// min(b^retries * d, limit)
|
||||||
|
//
|
||||||
|
// This function has a known overflow issue and should not be used anymore.
|
||||||
|
//
|
||||||
|
// Deprecated: Use BackoffFuncWithOpts from github.com/hetznercloud/hcloud-go/v2/hcloud instead.
|
||||||
func ExponentialBackoffWithLimit(b float64, d time.Duration, limit time.Duration) hcloud.BackoffFunc {
|
func ExponentialBackoffWithLimit(b float64, d time.Duration, limit time.Duration) hcloud.BackoffFunc {
|
||||||
return func(retries int) time.Duration {
|
return func(retries int) time.Duration {
|
||||||
current := time.Duration(math.Pow(b, float64(retries))) * d
|
current := time.Duration(math.Pow(b, float64(retries))) * d
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
serverTypePerArchitecture = map[hcloud.Architecture]*hcloud.ServerType{
|
serverTypePerArchitecture = map[hcloud.Architecture]*hcloud.ServerType{
|
||||||
hcloud.ArchitectureX86: {Name: "cx22"},
|
hcloud.ArchitectureX86: {Name: "cx23"},
|
||||||
hcloud.ArchitectureARM: {Name: "cax11"},
|
hcloud.ArchitectureARM: {Name: "cax11"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +94,10 @@ type UploadOptions struct {
|
||||||
// We also always add a label `apricote.de/created-by=hcloud-image-upload` ([CreatedByLabel], [CreatedByValue]).
|
// We also always add a label `apricote.de/created-by=hcloud-image-upload` ([CreatedByLabel], [CreatedByValue]).
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
|
|
||||||
|
// Location is the datacenter location for the temporary server.
|
||||||
|
// Defaults to fsn1 if not specified.
|
||||||
|
Location *hcloud.Location
|
||||||
|
|
||||||
// DebugSkipResourceCleanup will skip the cleanup of the temporary SSH Key and Server.
|
// DebugSkipResourceCleanup will skip the cleanup of the temporary SSH Key and Server.
|
||||||
DebugSkipResourceCleanup bool
|
DebugSkipResourceCleanup bool
|
||||||
}
|
}
|
||||||
|
|
@ -104,9 +108,10 @@ const (
|
||||||
CompressionNone Compression = ""
|
CompressionNone Compression = ""
|
||||||
CompressionBZ2 Compression = "bz2"
|
CompressionBZ2 Compression = "bz2"
|
||||||
CompressionXZ Compression = "xz"
|
CompressionXZ Compression = "xz"
|
||||||
|
CompressionZSTD Compression = "zstd"
|
||||||
|
|
||||||
// Possible future additions:
|
// Possible future additions:
|
||||||
// zip,zstd
|
// zip
|
||||||
)
|
)
|
||||||
|
|
||||||
type Format string
|
type Format string
|
||||||
|
|
@ -213,9 +218,14 @@ func (s *Client) Upload(ctx context.Context, options UploadOptions) (*hcloud.Ima
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location := defaultLocation
|
||||||
|
if options.Location != nil {
|
||||||
|
location = options.Location
|
||||||
|
}
|
||||||
|
|
||||||
logger.DebugContext(ctx, "creating server with config",
|
logger.DebugContext(ctx, "creating server with config",
|
||||||
"image", defaultImage.Name,
|
"image", defaultImage.Name,
|
||||||
"location", defaultLocation.Name,
|
"location", location.Name,
|
||||||
"serverType", serverType.Name,
|
"serverType", serverType.Name,
|
||||||
)
|
)
|
||||||
serverCreateResult, _, err := s.c.Server.Create(ctx, hcloud.ServerCreateOpts{
|
serverCreateResult, _, err := s.c.Server.Create(ctx, hcloud.ServerCreateOpts{
|
||||||
|
|
@ -229,7 +239,7 @@ func (s *Client) Upload(ctx context.Context, options UploadOptions) (*hcloud.Ima
|
||||||
StartAfterCreate: hcloud.Ptr(false),
|
StartAfterCreate: hcloud.Ptr(false),
|
||||||
// Image will never be booted, we only boot into rescue system
|
// Image will never be booted, we only boot into rescue system
|
||||||
Image: defaultImage,
|
Image: defaultImage,
|
||||||
Location: defaultLocation,
|
Location: location,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -316,7 +326,7 @@ func (s *Client) Upload(ctx context.Context, options UploadOptions) (*hcloud.Ima
|
||||||
|
|
||||||
err = control.Retry(
|
err = control.Retry(
|
||||||
contextlogger.New(ctx, logger.With("operation", "ssh")),
|
contextlogger.New(ctx, logger.With("operation", "ssh")),
|
||||||
10,
|
100, // ~ 3 minutes
|
||||||
func() error {
|
func() error {
|
||||||
var err error
|
var err error
|
||||||
logger.DebugContext(ctx, "trying to connect to server", "ip", server.PublicNet.IPv4.IP)
|
logger.DebugContext(ctx, "trying to connect to server", "ip", server.PublicNet.IPv4.IP)
|
||||||
|
|
@ -329,55 +339,42 @@ func (s *Client) Upload(ctx context.Context, options UploadOptions) (*hcloud.Ima
|
||||||
}
|
}
|
||||||
defer func() { _ = sshClient.Close() }()
|
defer func() { _ = sshClient.Close() }()
|
||||||
|
|
||||||
// 6. SSH On Server: Download Image, Decompress, Write to Root Disk
|
// 6. Wipe existing disk, to avoid storing any bytes from it in the snapshot
|
||||||
logger.InfoContext(ctx, "# Step 6: Downloading image and writing to disk")
|
logger.InfoContext(ctx, "# Step 6: Cleaning existing disk")
|
||||||
cmd := ""
|
|
||||||
if options.ImageURL != nil {
|
output, err := sshsession.Run(sshClient, "blkdiscard /dev/sda", nil)
|
||||||
cmd += fmt.Sprintf("wget --no-verbose -O - %q", options.ImageURL.String())
|
logger.DebugContext(ctx, string(output))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to clean existing disk: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.ImageCompression != CompressionNone {
|
// 7. SSH On Server: Download Image, Decompress, Write to Root Disk
|
||||||
switch options.ImageCompression {
|
logger.InfoContext(ctx, "# Step 7: Downloading image and writing to disk")
|
||||||
case CompressionBZ2:
|
|
||||||
cmd += " | bzip2 -cd"
|
cmd, err := assembleCommand(options)
|
||||||
case CompressionXZ:
|
if err != nil {
|
||||||
cmd += " | xz -cd"
|
return nil, err
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown compression: %q", options.ImageCompression)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch options.ImageFormat {
|
|
||||||
case FormatRaw:
|
|
||||||
cmd += " | dd of=/dev/sda bs=4M"
|
|
||||||
case FormatQCOW2:
|
|
||||||
cmd += " > image.qcow2 && qemu-img dd -f qcow2 -O raw if=image.qcow2 of=/dev/sda bs=4M"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd += " && sync"
|
|
||||||
|
|
||||||
// Make sure that we fail early, ie. if the image url does not work.
|
|
||||||
// the pipefail does not work correctly without wrapping in bash.
|
|
||||||
cmd = fmt.Sprintf("bash -c 'set -euo pipefail && %s'", cmd)
|
|
||||||
logger.DebugContext(ctx, "running download, decompress and write to disk command", "cmd", cmd)
|
logger.DebugContext(ctx, "running download, decompress and write to disk command", "cmd", cmd)
|
||||||
|
|
||||||
output, err := sshsession.Run(sshClient, cmd, options.ImageReader)
|
output, err = sshsession.Run(sshClient, cmd, options.ImageReader)
|
||||||
logger.InfoContext(ctx, "# Step 6: Finished writing image to disk")
|
logger.InfoContext(ctx, "# Step 7: Finished writing image to disk")
|
||||||
logger.DebugContext(ctx, string(output))
|
logger.DebugContext(ctx, string(output))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to download and write the image: %w", err)
|
return nil, fmt.Errorf("failed to download and write the image: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. SSH On Server: Shutdown
|
// 8. SSH On Server: Shutdown
|
||||||
logger.InfoContext(ctx, "# Step 7: Shutting down server")
|
logger.InfoContext(ctx, "# Step 8: Shutting down server")
|
||||||
_, err = sshsession.Run(sshClient, "shutdown now", nil)
|
_, err = sshsession.Run(sshClient, "shutdown now", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO Verify if shutdown error, otherwise return
|
// TODO Verify if shutdown error, otherwise return
|
||||||
logger.WarnContext(ctx, "shutdown returned error", "err", err)
|
logger.WarnContext(ctx, "shutdown returned error", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Create Image from Server
|
// 9. Create Image from Server
|
||||||
logger.InfoContext(ctx, "# Step 8: Creating Image")
|
logger.InfoContext(ctx, "# Step 9: Creating Image")
|
||||||
createImageResult, _, err := s.c.Server.CreateImage(ctx, server, &hcloud.ServerCreateImageOpts{
|
createImageResult, _, err := s.c.Server.CreateImage(ctx, server, &hcloud.ServerCreateImageOpts{
|
||||||
Type: hcloud.ImageTypeSnapshot,
|
Type: hcloud.ImageTypeSnapshot,
|
||||||
Description: options.Description,
|
Description: options.Description,
|
||||||
|
|
@ -522,3 +519,41 @@ func (s *Client) cleanupTempSSHKeys(ctx context.Context, logger *slog.Logger, se
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assembleCommand(options UploadOptions) (string, error) {
|
||||||
|
// Make sure that we fail early, ie. if the image url does not work
|
||||||
|
cmd := "set -euo pipefail && "
|
||||||
|
|
||||||
|
if options.ImageURL != nil {
|
||||||
|
cmd += fmt.Sprintf("wget --no-verbose -O - %q | ", options.ImageURL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.ImageCompression != CompressionNone {
|
||||||
|
switch options.ImageCompression {
|
||||||
|
case CompressionBZ2:
|
||||||
|
cmd += "bzip2 -cd | "
|
||||||
|
case CompressionXZ:
|
||||||
|
cmd += "xz -cd | "
|
||||||
|
case CompressionZSTD:
|
||||||
|
cmd += "zstd -cd | "
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown compression: %q", options.ImageCompression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch options.ImageFormat {
|
||||||
|
case FormatRaw:
|
||||||
|
cmd += "dd of=/dev/sda bs=4M"
|
||||||
|
case FormatQCOW2:
|
||||||
|
cmd += "tee image.qcow2 > /dev/null && qemu-img dd -f qcow2 -O raw if=image.qcow2 of=/dev/sda bs=4M"
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown format: %q", options.ImageFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd += " && sync"
|
||||||
|
|
||||||
|
// the pipefail does not work correctly without wrapping in bash.
|
||||||
|
cmd = fmt.Sprintf("bash -c '%s'", cmd)
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,125 @@
|
||||||
package hcloudimages_test
|
package hcloudimages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"testing"
|
||||||
"github.com/hetznercloud/hcloud-go/v2/hcloud"
|
|
||||||
|
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleClient_Upload() {
|
func mustParseURL(s string) *url.URL {
|
||||||
client := hcloudimages.NewClient(
|
u, err := url.Parse(s)
|
||||||
hcloud.NewClient(hcloud.WithToken("<your token>")),
|
|
||||||
)
|
|
||||||
|
|
||||||
imageURL, err := url.Parse("https://example.com/disk-image.raw.bz2")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
image, err := client.Upload(context.TODO(), hcloudimages.UploadOptions{
|
return u
|
||||||
ImageURL: imageURL,
|
}
|
||||||
ImageCompression: hcloudimages.CompressionBZ2,
|
|
||||||
Architecture: hcloud.ArchitectureX86,
|
func TestAssembleCommand(t *testing.T) {
|
||||||
})
|
tests := []struct {
|
||||||
if err != nil {
|
name string
|
||||||
panic(err)
|
options UploadOptions
|
||||||
}
|
want string
|
||||||
|
wantErr bool
|
||||||
fmt.Printf("Uploaded Image: %d", image.ID)
|
}{
|
||||||
|
{
|
||||||
|
name: "local raw",
|
||||||
|
options: UploadOptions{},
|
||||||
|
want: "bash -c 'set -euo pipefail && dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote raw",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageURL: mustParseURL("https://example.com/image.xz"),
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && wget --no-verbose -O - \"https://example.com/image.xz\" | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local xz",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageCompression: CompressionXZ,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && xz -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote xz",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageURL: mustParseURL("https://example.com/image.xz"),
|
||||||
|
ImageCompression: CompressionXZ,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && wget --no-verbose -O - \"https://example.com/image.xz\" | xz -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local zstd",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageCompression: CompressionZSTD,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && zstd -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote zstd",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageURL: mustParseURL("https://example.com/image.zst"),
|
||||||
|
ImageCompression: CompressionZSTD,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && wget --no-verbose -O - \"https://example.com/image.zst\" | zstd -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local bz2",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageCompression: CompressionBZ2,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && bzip2 -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote bz2",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageURL: mustParseURL("https://example.com/image.bz2"),
|
||||||
|
ImageCompression: CompressionBZ2,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && wget --no-verbose -O - \"https://example.com/image.bz2\" | bzip2 -cd | dd of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local qcow2",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageFormat: FormatQCOW2,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && tee image.qcow2 > /dev/null && qemu-img dd -f qcow2 -O raw if=image.qcow2 of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote qcow2",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageURL: mustParseURL("https://example.com/image.qcow2"),
|
||||||
|
ImageFormat: FormatQCOW2,
|
||||||
|
},
|
||||||
|
want: "bash -c 'set -euo pipefail && wget --no-verbose -O - \"https://example.com/image.qcow2\" | tee image.qcow2 > /dev/null && qemu-img dd -f qcow2 -O raw if=image.qcow2 of=/dev/sda bs=4M && sync'",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "unknown compression",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageCompression: "noodle",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "unknown format",
|
||||||
|
options: UploadOptions{
|
||||||
|
ImageFormat: "poodle",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := assembleCommand(tt.options)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("assembleCommand() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("assembleCommand() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
hcloudimages/doc_test.go
Normal file
34
hcloudimages/doc_test.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package hcloudimages_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hetznercloud/hcloud-go/v2/hcloud"
|
||||||
|
|
||||||
|
"github.com/apricote/hcloud-upload-image/hcloudimages"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleClient_Upload() {
|
||||||
|
client := hcloudimages.NewClient(
|
||||||
|
hcloud.NewClient(hcloud.WithToken("<your token>")),
|
||||||
|
)
|
||||||
|
|
||||||
|
imageURL, err := url.Parse("https://example.com/disk-image.raw.bz2")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := client.Upload(context.TODO(), hcloudimages.UploadOptions{
|
||||||
|
ImageURL: imageURL,
|
||||||
|
ImageCompression: hcloudimages.CompressionBZ2,
|
||||||
|
Architecture: hcloud.ArchitectureX86,
|
||||||
|
Location: &hcloud.Location{Name: "nbg1"}, // Optional: defaults to fsn1
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Uploaded Image: %d", image.ID)
|
||||||
|
}
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
module github.com/apricote/hcloud-upload-image/hcloudimages
|
module github.com/apricote/hcloud-upload-image/hcloudimages
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.24.2
|
toolchain go1.25.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.11.1
|
||||||
golang.org/x/crypto v0.37.0
|
golang.org/x/crypto v0.46.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.21.1 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.1 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0 h1:wUpQT+fgAxIcdMtFvuCJ78ziqc/VARubpOQPQyj4Q84=
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0 h1:mxasKipFPDPzni85xcMgwYci2PH8TalLgyPJoTlhWDA=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.21.0/go.mod h1:WSM7w+9tT86sJTNcF8a/oHljC3HUmQfcLxYsgx6PpSc=
|
github.com/hetznercloud/hcloud-go/v2 v2.34.0/go.mod h1:aF9x0XYNRRQ7N4gux/4cEJeGAHcggu7sX+BBA1rv8ks=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
|
@ -20,30 +20,34 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages/backoff"
|
"github.com/hetznercloud/hcloud-go/v2/hcloud"
|
||||||
|
|
||||||
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
|
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,7 +19,7 @@ func Retry(ctx context.Context, maxTries int, f func() error) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
backoffFunc := backoff.ExponentialBackoffWithLimit(2, 200*time.Millisecond, 2*time.Second)
|
backoffFunc := hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{Multiplier: 2, Base: 200 * time.Millisecond, Cap: 2 * time.Second})
|
||||||
|
|
||||||
for try := 0; try < maxTries; try++ {
|
for try := 0; try < maxTries; try++ {
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package version
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// version is a semver version (https://semver.org).
|
// version is a semver version (https://semver.org).
|
||||||
version = "1.0.0" // x-release-please-version
|
version = "1.3.0" // x-release-please-version
|
||||||
|
|
||||||
// versionPrerelease is a semver version pre-release identifier (https://semver.org).
|
// versionPrerelease is a semver version pre-release identifier (https://semver.org).
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,15 @@
|
||||||
"gomodTidy",
|
"gomodTidy",
|
||||||
"gomodUpdateImportPaths"
|
"gomodUpdateImportPaths"
|
||||||
],
|
],
|
||||||
"goGetDirs": ["./...", "./hcloudimages/..."],
|
"goGetDirs": [
|
||||||
|
"./...",
|
||||||
|
"./hcloudimages/..."
|
||||||
|
],
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"fileMatch": [
|
"managerFilePatterns": [
|
||||||
"^\\.github\\/(?:workflows|actions)\\/.+\\.ya?ml$"
|
"/^\\.github\\/(?:workflows|actions)\\/.+\\.ya?ml$/"
|
||||||
],
|
],
|
||||||
"matchStrings": [
|
"matchStrings": [
|
||||||
"(?:version|VERSION): (?<currentValue>.+) # renovate: datasource=(?<datasource>[a-z-]+) depName=(?<depName>.+)(?: packageName=(?<packageName>.+))?(?: versioning=(?<versioning>[a-z-]+))?"
|
"(?:version|VERSION): (?<currentValue>.+) # renovate: datasource=(?<datasource>[a-z-]+) depName=(?<depName>.+)(?: packageName=(?<packageName>.+))?(?: versioning=(?<versioning>[a-z-]+))?"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue