Skip to content

Commit d3c679a

Browse files
Merge branch 'lxc:main' into stable-test-final-pr
2 parents e9ee2d7 + d7d4a26 commit d3c679a

File tree

222 files changed

+10177
-7654
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

222 files changed

+10177
-7654
lines changed

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ By default, any contribution to this project is made under the Apache
1818
The author of a change remains the copyright holder of their code
1919
(no copyright assignment).
2020

21+
## No Large Language Models (LLMs) or similar AI tools
22+
23+
All contributions to this project are expected to be done by human
24+
beings or through standard predictable tooling (e.g. scripts, formatters, ...).
25+
26+
We expect all contributors to be able to reason about the code that they
27+
contribute and explain why they're taking a particular approach.
28+
29+
LLMs and similar predictive tools have the annoying tendency of
30+
producing large amount of low quality code with subtle issues which end
31+
up taking the maintainers more time to debug than it would have taken to
32+
write the code by hand in the first place.
33+
34+
Any attempt at hiding the use of LLMs or similar tools in Incus contributions
35+
will result in a revert of the affected changes and a ban from the project.
36+
2137
## Pull requests
2238

2339
Changes to this project should be proposed as pull requests on GitHub

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ ifneq "$(INCUS_OFFLINE)" ""
9595
endif
9696
$(GO) get -t -v -u ./...
9797
$(GO) get github.com/go-jose/go-jose/[email protected]
98+
$(GO) get github.com/olekukonko/[email protected]
9899
$(GO) mod tidy --go=1.23.7
99100
$(GO) get toolchain@none
100101

@@ -306,7 +307,6 @@ endif
306307
ifeq ($(shell command -v shellcheck),)
307308
echo "Please install shellcheck"
308309
exit 1
309-
else
310310
endif
311311
ifeq ($(shell command -v flake8),)
312312
echo "Please install flake8"

client/incus_network_address_sets.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func (r *ProtocolIncus) GetNetworkAddressSets() ([]api.NetworkAddressSet, error)
4444

4545
// GetNetworkAddressSetsAllProjects returns a list of network address set structs across all projects.
4646
func (r *ProtocolIncus) GetNetworkAddressSetsAllProjects() ([]api.NetworkAddressSet, error) {
47-
if !r.HasExtension("network_address_sets_all_projects") {
48-
return nil, fmt.Errorf(`The server is missing the required "network_address_sets_all_projects" API extension`)
47+
if !r.HasExtension("network_address_set") {
48+
return nil, fmt.Errorf(`The server is missing the required "network_address_set" API extension`)
4949
}
5050

5151
addressSets := []api.NetworkAddressSet{}

client/incus_server.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,16 @@ func (r *ProtocolIncus) ApplyServerPreseed(config api.InitPreseed) error {
537537
}
538538
}
539539

540+
// Apply certificate configuration.
541+
if len(config.Server.Certificates) > 0 {
542+
for _, certificate := range config.Server.Certificates {
543+
err := r.CreateCertificate(certificate)
544+
if err != nil {
545+
return fmt.Errorf("Failed to create certificate %q: %w", certificate.Name, err)
546+
}
547+
}
548+
}
549+
540550
// Cluster configuration.
541551
if config.Cluster != nil && config.Cluster.Enabled {
542552
// Get the current cluster configuration

client/incus_storage_volumes.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package incus
33
import (
44
"fmt"
55
"io"
6+
"net"
67
"net/http"
78
"net/url"
89
"strings"
910

11+
"github.com/pkg/sftp"
12+
1013
"github.com/lxc/incus/v6/shared/api"
1114
"github.com/lxc/incus/v6/shared/cancel"
1215
"github.com/lxc/incus/v6/shared/ioprogress"
@@ -1119,3 +1122,44 @@ func (r *ProtocolIncus) CreateStoragePoolVolumeFromBackup(pool string, args Stor
11191122

11201123
return &op, nil
11211124
}
1125+
1126+
// GetStoragePoolVolumeFileSFTPConn returns a connection to the volume's SFTP endpoint.
1127+
func (r *ProtocolIncus) GetStoragePoolVolumeFileSFTPConn(pool string, volType string, volName string) (net.Conn, error) {
1128+
if !r.HasExtension("custom_volume_sftp") {
1129+
return nil, fmt.Errorf(`The server is missing the required "custom_volume_sftp" API extension`)
1130+
}
1131+
1132+
u := api.NewURL()
1133+
u.URL = r.httpBaseURL // Preload the URL with the client base URL.
1134+
u.Path("1.0", "storage-pools", pool, "volumes", volType, volName, "sftp")
1135+
r.setURLQueryAttributes(&u.URL)
1136+
1137+
return r.rawSFTPConn(&u.URL)
1138+
}
1139+
1140+
// GetStoragePoolVolumeFileSFTP returns an SFTP connection to the volume.
1141+
func (r *ProtocolIncus) GetStoragePoolVolumeFileSFTP(pool string, volType string, volName string) (*sftp.Client, error) {
1142+
if !r.HasExtension("custom_volume_sftp") {
1143+
return nil, fmt.Errorf(`The server is missing the required "custom_volume_sftp" API extension`)
1144+
}
1145+
1146+
conn, err := r.GetStoragePoolVolumeFileSFTPConn(pool, volType, volName)
1147+
if err != nil {
1148+
return nil, err
1149+
}
1150+
1151+
// Get a SFTP client.
1152+
client, err := sftp.NewClientPipe(conn, conn, sftp.MaxPacketUnchecked(128*1024))
1153+
if err != nil {
1154+
_ = conn.Close()
1155+
return nil, err
1156+
}
1157+
1158+
go func() {
1159+
// Wait for the client to be done before closing the connection.
1160+
_ = client.Wait()
1161+
_ = conn.Close()
1162+
}()
1163+
1164+
return client, nil
1165+
}

client/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,10 @@ type InstanceServer interface {
388388
CreateStoragePoolVolumeFromISO(pool string, args StorageVolumeBackupArgs) (op Operation, err error)
389389
CreateStoragePoolVolumeFromMigration(pool string, volume api.StorageVolumesPost) (op Operation, err error)
390390

391+
// Storage volume SFTP functions ("custom_volume_sftp" API extension)
392+
GetStoragePoolVolumeFileSFTPConn(pool string, volType string, volName string) (net.Conn, error)
393+
GetStoragePoolVolumeFileSFTP(pool string, volType string, volName string) (*sftp.Client, error)
394+
391395
// Cluster functions ("cluster" API extensions)
392396
GetCluster() (cluster *api.Cluster, ETag string, err error)
393397
UpdateCluster(cluster api.ClusterPut, ETag string) (op Operation, err error)

client/oci_images.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,6 @@ func (r *ProtocolOCI) GetImageFile(fingerprint string, req ImageFileRequest) (*I
155155
return nil, fmt.Errorf("OCI image export currently requires root access")
156156
}
157157

158-
_, err = exec.LookPath("umoci")
159-
if err != nil {
160-
return nil, fmt.Errorf("OCI container handling requires \"umoci\" be present on the system")
161-
}
162-
163158
// Get some temporary storage.
164159
ociPath, err := os.MkdirTemp("", "incus-oci-")
165160
if err != nil {
@@ -183,6 +178,8 @@ func (r *ProtocolOCI) GetImageFile(fingerprint string, req ImageFileRequest) (*I
183178
req.ProgressHandler(ioprogress.ProgressData{Text: "Retrieving OCI image from registry"})
184179
}
185180

181+
imageTag := "latest"
182+
186183
stdout, _, err := subprocess.RunCommandSplit(
187184
ctx,
188185
env,
@@ -192,7 +189,7 @@ func (r *ProtocolOCI) GetImageFile(fingerprint string, req ImageFileRequest) (*I
192189
"copy",
193190
"--remove-signatures",
194191
fmt.Sprintf("%s/%s", strings.Replace(r.httpHost, "https://", "docker://", 1), info.Alias),
195-
fmt.Sprintf("oci:%s:latest", filepath.Join(ociPath, "oci")))
192+
fmt.Sprintf("oci:%s:%s", filepath.Join(ociPath, "oci"), imageTag))
196193
if err != nil {
197194
logger.Debug("Error copying remote image to local", logger.Ctx{"image": info.Alias, "stdout": stdout, "stderr": err})
198195
return nil, err
@@ -203,14 +200,9 @@ func (r *ProtocolOCI) GetImageFile(fingerprint string, req ImageFileRequest) (*I
203200
req.ProgressHandler(ioprogress.ProgressData{Text: "Unpacking the OCI image"})
204201
}
205202

206-
stdout, err = subprocess.RunCommand(
207-
"umoci",
208-
"unpack",
209-
"--keep-dirlinks",
210-
"--image", filepath.Join(ociPath, "oci"),
211-
filepath.Join(ociPath, "image"))
203+
err = unpackOCIImage(filepath.Join(ociPath, "oci"), imageTag, filepath.Join(ociPath, "image"))
212204
if err != nil {
213-
logger.Debug("Error unpacking OCI image", logger.Ctx{"image": filepath.Join(ociPath, "oci"), "stdout": stdout, "stderr": err})
205+
logger.Debug("Error unpacking OCI image", logger.Ctx{"image": filepath.Join(ociPath, "oci"), "err": err})
214206
return nil, err
215207
}
216208

client/oci_util.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build !linux
2+
3+
package incus
4+
5+
import (
6+
"fmt"
7+
)
8+
9+
func unpackOCIImage(imagePath string, imageTag string, bundlePath string) error {
10+
return fmt.Errorf("Platform isn't supported")
11+
}

client/oci_util_linux.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//go:build linux
2+
3+
package incus
4+
5+
import (
6+
"fmt"
7+
8+
"github.com/apex/log"
9+
"github.com/opencontainers/umoci"
10+
"github.com/opencontainers/umoci/oci/cas/dir"
11+
"github.com/opencontainers/umoci/oci/casext"
12+
"github.com/opencontainers/umoci/oci/layer"
13+
14+
"github.com/lxc/incus/v6/shared/logger"
15+
)
16+
17+
// Custom handler to intercept logs.
18+
type umociLogHandler struct {
19+
Message string
20+
}
21+
22+
// HandleLog implements a proxy between apex/log and our logger.
23+
func (h *umociLogHandler) HandleLog(e *log.Entry) error {
24+
switch e.Level {
25+
case log.DebugLevel:
26+
logger.Debug(h.Message, logger.Ctx{"log": e.Message})
27+
case log.InfoLevel:
28+
logger.Info(h.Message, logger.Ctx{"log": e.Message})
29+
case log.WarnLevel:
30+
logger.Warn(h.Message, logger.Ctx{"log": e.Message})
31+
case log.ErrorLevel:
32+
logger.Error(h.Message, logger.Ctx{"log": e.Message})
33+
case log.FatalLevel:
34+
logger.Panic(h.Message, logger.Ctx{"log": e.Message})
35+
default:
36+
logger.Error("Unknown umoci log level", logger.Ctx{"log": e.Message})
37+
}
38+
39+
return nil
40+
}
41+
42+
func unpackOCIImage(imagePath string, imageTag string, bundlePath string) error {
43+
// Set the custom handler
44+
log.SetHandler(&umociLogHandler{Message: "Unpacking OCI image"})
45+
defer log.SetHandler(nil)
46+
47+
var unpackOptions layer.UnpackOptions
48+
unpackOptions.KeepDirlinks = true
49+
50+
// Get a reference to the CAS.
51+
engine, err := dir.Open(imagePath)
52+
if err != nil {
53+
return fmt.Errorf("Open CAS: %w", err)
54+
}
55+
56+
engineExt := casext.NewEngine(engine)
57+
defer func() { _ = engine.Close() }()
58+
59+
return umoci.Unpack(engineExt, imageTag, bundlePath, unpackOptions)
60+
}

cmd/generate-config/incus_doc.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func getSortedKeysFromMap[K string, V IterableAny](m map[K]V) []K {
6969
keys = append(keys, k)
7070
}
7171

72-
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
72+
slices.Sort(keys)
7373
return keys
7474
}
7575

@@ -262,7 +262,7 @@ func writeDocFile(inputJSONPath, outputTxtPath string) error {
262262
countMaxBackTicks := func(s string) int {
263263
count, curr_count := 0, 0
264264
n := len(s)
265-
for i := 0; i < n; i++ {
265+
for i := range n {
266266
if s[i] == '`' {
267267
curr_count++
268268
continue

cmd/generate-database/db.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ func generate(pkgs []string, boilerplateFilename string) error {
132132
// Lazy matching for prefix, does not consider Go syntax and therefore
133133
// lines starting with prefix, that are part of e.g. multiline strings
134134
// match as well. This is highly unlikely to cause false positives.
135-
if strings.HasPrefix(line, prefix) {
136-
line = strings.TrimPrefix(line, prefix)
135+
after, ok := strings.CutPrefix(line, prefix)
136+
if ok {
137+
line = after
137138

138139
// Use csv parser to properly handle arguments surrounded by double quotes.
139140
r := csv.NewReader(strings.NewReader(line))

cmd/generate-database/db/method.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ func (m *Method) getRefs(buf *file.Buffer, parentTable string, refMapping *Mappi
621621

622622
switch refMapping.Type {
623623
case ReferenceTable:
624-
buf.L("%s, err := Get%s(ctx, db, \"%s\", \"%s\", filters...)", refParentList, lex.Plural(refStruct), parentTable, m.entity)
624+
buf.L("%s, err := Get%s(ctx, db, \"%s\", \"%s\", filters...)", refParentList, lex.Plural(refStruct), parentTable, lex.SnakeCase(m.entity))
625625
m.ifErrNotNil(buf, true, "nil", "err")
626626
buf.L("%s := map[string]%s{}", refList, refMapping.ImportType())
627627
buf.L("for _, ref := range %s[%sID] {", refParentList, refParent)
@@ -634,7 +634,7 @@ func (m *Method) getRefs(buf *file.Buffer, parentTable string, refMapping *Mappi
634634
buf.L("}")
635635
buf.N()
636636
case MapTable:
637-
buf.L("%s, err := Get%s(ctx, db, \"%s\", \"%s\", filters...)", refParentList, lex.Plural(refStruct), parentTable, m.entity)
637+
buf.L("%s, err := Get%s(ctx, db, \"%s\", \"%s\", filters...)", refParentList, lex.Plural(refStruct), parentTable, lex.SnakeCase(m.entity))
638638
m.ifErrNotNil(buf, true, "nil", "err")
639639
buf.L("%s, ok := %s[%sID]", refList, refParentList, refParent)
640640
buf.L("if !ok {")
@@ -1023,7 +1023,7 @@ func (m *Method) createRefs(buf *file.Buffer, parentTable string, refMapping *Ma
10231023
buf.L("%s[key] = %s", lex.Plural(refVar), refVar)
10241024
buf.L("}")
10251025
buf.N()
1026-
buf.L("err := Create%s(ctx, db, \"%s\", \"%s\", %s)", lex.Plural(refStruct), parentTable, m.entity, lex.Plural(refVar))
1026+
buf.L("err := Create%s(ctx, db, \"%s\", \"%s\", %s)", lex.Plural(refStruct), parentTable, lex.SnakeCase(m.entity), lex.Plural(refVar))
10271027
m.ifErrNotNil(buf, false, fmt.Sprintf("fmt.Errorf(\"Insert %s failed for %s: %%w\", err)", refStruct, lex.PascalCase(m.entity)))
10281028
case MapTable:
10291029
buf.L("referenceID := int(%sID)", refParent)
@@ -1035,7 +1035,7 @@ func (m *Method) createRefs(buf *file.Buffer, parentTable string, refMapping *Ma
10351035

10361036
buf.L("}")
10371037
buf.N()
1038-
buf.L("err := Create%s(ctx, db, \"%s\", \"%s\", insert)", refStruct, parentTable, m.entity)
1038+
buf.L("err := Create%s(ctx, db, \"%s\", \"%s\", insert)", refStruct, parentTable, lex.SnakeCase(m.entity))
10391039
m.ifErrNotNil(buf, true, fmt.Sprintf("fmt.Errorf(\"Insert %s failed for %s: %%w\", err)", refStruct, lex.PascalCase(m.entity)))
10401040
buf.L("}")
10411041
}
@@ -1277,7 +1277,7 @@ func (m *Method) updateRefs(buf *file.Buffer, parentTable string, refMapping *Ma
12771277
refList := lex.Plural(refVar)
12781278
refParent := lex.CamelCase(m.entity)
12791279

1280-
buf.L("err := Update%s(ctx, db, \"%s\", \"%s\", int(%sID), %s)", lex.Plural(refStruct), parentTable, m.entity, refParent, refList)
1280+
buf.L("err := Update%s(ctx, db, \"%s\", \"%s\", int(%sID), %s)", lex.Plural(refStruct), parentTable, lex.SnakeCase(m.entity), refParent, refList)
12811281
m.ifErrNotNil(buf, true, fmt.Sprintf("fmt.Errorf(\"Replace %s for %s failed: %%w\", err)", refStruct, lex.PascalCase(m.entity)))
12821282
buf.L("return nil")
12831283

cmd/generate-database/db/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func findStruct(scope *types.Scope, name string) *types.Struct {
424424
func parseStruct(str *types.Struct, kind string, pkgName string) ([]*Field, error) {
425425
fields := make([]*Field, 0)
426426

427-
for i := 0; i < str.NumFields(); i++ {
427+
for i := range str.NumFields() {
428428
f := str.Field(i)
429429
if f.Embedded() {
430430
// Check if this is a parent struct.

cmd/incus-agent/api_1.0.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ func api10Put(d *Daemon, r *http.Request) response.Response {
111111
}
112112

113113
func startDevIncusServer(d *Daemon) error {
114+
if !osGuestAPISupport {
115+
return nil
116+
}
117+
114118
d.DevIncusMu.Lock()
115119
defer d.DevIncusMu.Unlock()
116120

@@ -148,6 +152,10 @@ func startDevIncusServer(d *Daemon) error {
148152
}
149153

150154
func stopDevIncusServer(d *Daemon) error {
155+
if !osGuestAPISupport {
156+
return nil
157+
}
158+
151159
d.DevIncusMu.Lock()
152160
d.DevIncusRunning = false
153161
d.DevIncusMu.Unlock()

cmd/incus-agent/dev_incus.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ var DevIncusMetadataGet = devIncusHandler{"/1.0/meta-data", func(d *Daemon, w ht
121121
var client incus.InstanceServer
122122
var err error
123123

124-
for i := 0; i < 10; i++ {
124+
for range 10 {
125125
client, err = getVsockClient(d)
126126
if err == nil {
127127
break

cmd/incus-agent/events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func eventsProcess(event api.Event) {
148148
// Attempt to perform the mount.
149149
mntSource := fmt.Sprintf("incus_%s", e.Name)
150150

151-
for i := 0; i < 20; i++ {
151+
for range 20 {
152152
time.Sleep(500 * time.Millisecond)
153153

154154
err = osMountShared(mntSource, e.Config["path"], "virtiofs", nil)

0 commit comments

Comments
 (0)