Merge pull request #5449 from mie313/hcs-driver

Implement HCS driver
This commit is contained in:
Akihiro Suda authored and GitHub committed 2026-09-01 14:56:41 +09:00
commit dd909d0973
27 files changed
+1700 -23

No files matched your search

+4 -2
View File
@@ -644,10 +644,12 @@ shfmt:
.PHONY: go-licenses
go-licenses:
# the allow list corresponds to https://github.com/cncf/foundation/blob/e5db022a0009f4db52b89d9875640cf3137153fe/allowed-third-party-license-policy.md
# the allow list corresponds to https://github.com/cncf/foundation/blob/87e70a07d5a0cf06cd6f8208b8d52c16f035724a/policies-guidance/allowed-third-party-license-policy.md
# hashicorp/hcl/v2 is MPL-2.0; covered by the CNCF license exception for hashicorp/hcl
# see also https://github.com/cncf/foundation/issues/1242
go-licenses check --include_tests --ignore github.com/hashicorp/hcl/v2 ./... --allowed_licenses=$$(cat ./hack/allowed-licenses.txt)
# segmentio/asm is MIT-0; approved by CNCF, but go-licenses has not support it yet.
# see also https://github.com/google/licenseclassifier/issues/75
go-licenses check --include_tests --ignore github.com/hashicorp/hcl/v2 --ignore github.com/segmentio/asm ./... --allowed_licenses=$$(cat ./hack/allowed-licenses.txt)
.PHONY: gosocialcheck
gosocialcheck:
+9
View File
@@ -0,0 +1,9 @@
//go:build !external_hcs
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package main
// Import hcs driver to register it in the registry on windows.
import _ "github.com/lima-vm/lima/v2/pkg/driver/hcs"
File renamed without changes.
+9 -2
View File
@@ -18,7 +18,8 @@ require (
al.essio.dev/pkg/shellescape v1.6.0
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/Code-Hex/vz/v3 v3.7.1 // gomodjail:unconfined
github.com/Microsoft/go-winio v0.6.2 // gomodjail:unconfined
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // gomodjail:unconfined
github.com/Microsoft/hcsshim v0.14.1
github.com/apparentlymart/go-cidr v1.1.1
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e
github.com/cheggaaa/pb/v3 v3.2.1 // gomodjail:unconfined
@@ -81,7 +82,11 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/containerd/cgroups/v3 v3.1.3 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/typeurl/v2 v2.3.0 // indirect
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/djherbis/times v1.6.0 // indirect
@@ -91,6 +96,7 @@ require (
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/jsonschema-go v0.4.3 // indirect
@@ -114,13 +120,14 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
github.com/segmentio/asm v1.1.3 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
// gomodjail:unconfined
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/yuin/gopher-lua v1.1.2 // indirect
github.com/zclconf/go-cty v1.19.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
+93 -4
View File
@@ -1,13 +1,17 @@
al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=
al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Code-Hex/go-infinity-channel v1.0.0 h1:M8BWlfDOxq9or9yvF9+YkceoTkDI1pFAqvnP87Zh0Nw=
github.com/Code-Hex/go-infinity-channel v1.0.0/go.mod h1:5yUVg/Fqao9dAjcpzoQ33WwfdMWmISOrQloDRn3bsvY=
github.com/Code-Hex/vz/v3 v3.7.1 h1:EN1yNiyrbPq+dl388nne2NySo8I94EnPppvqypA65XM=
github.com/Code-Hex/vz/v3 v3.7.1/go.mod h1:1LsW0jqW0r0cQ+IeR4hHbjdqOtSidNCVMWhStMHGho8=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
github.com/Microsoft/hcsshim v0.14.1 h1:CMuB3fqQVfPdhyXhUqYdUmPUIOhJkmghCx3dJet8Cqs=
github.com/Microsoft/hcsshim v0.14.1/go.mod h1:VnzvPLyWUhxiPVsJ31P6XadxCcTogTguBFDy/1GR/OM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@@ -39,18 +43,29 @@ github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb/v3 v3.2.1 h1:aprZbFRG+B7+ug76S8QZ6Y1PW168UHzOmbC3wa+aU6I=
github.com/cheggaaa/pb/v3 v3.2.1/go.mod h1:U9hSVxoKqJrZIE3PkFG1xXXNaK/Ilzg+EF/scpXzEdw=
github.com/cilium/ebpf v0.22.0 h1:v2ktp0roffpMOj2MMf3idtCQZOsAoC4BJbAJN+ke2bY=
github.com/cilium/ebpf v0.22.0/go.mod h1:CDzZbe2hC5JjlDC+CY3KFCzlYwN4gbxppYM+Z10bQt4=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/typeurl/v2 v2.3.0 h1:HZHPhRWo5XMy3QGQoPrUzbW/2ckwjfweHmOwlkIrPAQ=
github.com/containerd/typeurl/v2 v2.3.0/go.mod h1:Qk+PAdUYArVj41TnGi6rJ+48RF0PkcTc4i/taoBcK0w=
github.com/containers/gvisor-tap-vsock v0.8.9 h1:6b7pqxFcKJ0EycBt1V4zPo3FQtgLLgs50AYkbFIb9eU=
github.com/containers/gvisor-tap-vsock v0.8.9/go.mod h1:OfqLraPkar5xMQcGbl9czDDSM6/xelt0HJpyB3es6v0=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
@@ -84,6 +99,10 @@ github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCG
github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 h1:x5yxNrq8XffV/OoNUeFPM6hxHVi5OTspSTBxr/9pemg=
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0=
@@ -107,10 +126,30 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
@@ -119,6 +158,7 @@ github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+
github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/yamlfmt v0.21.0 h1:9FKApQkDpMKgBjwLFytBHUCgqnQgxaQnci0uiESfbzs=
@@ -218,6 +258,7 @@ github.com/pkg/sftp v1.13.11/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hs
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@@ -228,8 +269,8 @@ github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDj
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
github.com/sethvargo/go-password v0.4.0 h1:eSidVKQw5C7CmTDAtH3RipBTSjdU1ZRxQaynD2GWLVU=
@@ -242,8 +283,13 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
@@ -260,6 +306,8 @@ github.com/zclconf/go-cty v1.19.0 h1:IV8WdqYZc2c5rLX9bEoLNXKojBAp0MZPBHMIrCoa/s4
github.com/zclconf/go-cty v1.19.0/go.mod h1:12W89jGn3JCOIQi7infWr9m80rOkb5RNYJqXMZcN4c8=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
@@ -278,12 +326,17 @@ go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -293,8 +346,13 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@@ -305,8 +363,11 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -316,10 +377,12 @@ golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -357,6 +420,10 @@ golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -368,13 +435,33 @@ golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@@ -390,3 +477,5 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f h1:O2w2DymsOlM/nv2pLNWCMCYOldgBBMkD7H0/prN5W2k=
gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+1 -1
View File
@@ -1 +1 @@
Apache-2.0,BSD-2-Clause,BSD-2-Clause-FreeBSD,BSD-3-Clause,MIT,ISC,Python-2.0,PostgreSQL,X11,Zlib
Apache-2.0,BSD-2-Clause,BSD-2-Clause-FreeBSD,BSD-3-Clause,MIT,MIT-0,ISC,Python-2.0,PostgreSQL,X11,Zlib
+1 -1
View File
@@ -294,7 +294,7 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
}
case firstUsernetIndex != -1 || *instConfig.VMType == limatype.VZ:
args.DNSAddresses = append(args.DNSAddresses, args.SlirpDNS)
case *instConfig.HostResolver.Enabled:
case limayaml.HostResolverEnabled(instConfig):
args.UDPDNSLocalPort = udpDNSLocalPort
args.TCPDNSLocalPort = tcpDNSLocalPort
args.DNSAddresses = append(args.DNSAddresses, args.SlirpDNS)
+1 -1
View File
@@ -119,7 +119,7 @@ type ConfiguredDriver struct {
// the instance directory and the driver's capability flags. It is returned by
// Driver.Info and, for external drivers, carried as JSON in InfoResponse.
type Info struct {
// Name is the driver name, e.g. "qemu", "vz", "wsl2" or "krunkit".
// Name is the driver name, e.g. "qemu", "vz", "wsl2", "hcs" or "krunkit".
Name string `json:"name"`
// VsockPort is the vsock port used by the guest agent, or 0 when vsock is
// not used.
+8
View File
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import "errors"
var errUnimplemented = errors.New("unimplemented")
+286
View File
@@ -0,0 +1,286 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"context"
"fmt"
"syscall"
"time"
"unsafe"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
// ---------------------------------------------------------------------------
// computecore.dll (HCS) bindings
// ---------------------------------------------------------------------------
var (
modcomputecore = windows.NewLazySystemDLL("computecore.dll")
procHcsCreateOperation = modcomputecore.NewProc("HcsCreateOperation")
procHcsCloseOperation = modcomputecore.NewProc("HcsCloseOperation")
procHcsWaitForOperationResult = modcomputecore.NewProc("HcsWaitForOperationResult")
procHcsCreateComputeSystem = modcomputecore.NewProc("HcsCreateComputeSystem")
procHcsStartComputeSystem = modcomputecore.NewProc("HcsStartComputeSystem")
procHcsTerminateComputeSystem = modcomputecore.NewProc("HcsTerminateComputeSystem")
procHcsCloseComputeSystem = modcomputecore.NewProc("HcsCloseComputeSystem")
procHcsGrantVMAccess = modcomputecore.NewProc("HcsGrantVmAccess")
procHcsOpenComputeSystem = modcomputecore.NewProc("HcsOpenComputeSystem")
procHcsWaitForComputeSystemExit = modcomputecore.NewProc("HcsWaitForComputeSystemExit")
procHcsGetComputeSystemProperties = modcomputecore.NewProc("HcsGetComputeSystemProperties")
modapiMsWinCoreComL110 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll")
procCoTaskMemFree = modapiMsWinCoreComL110.NewProc("CoTaskMemFree")
)
type (
hcsOperation syscall.Handle
hcsSystem syscall.Handle
)
const (
infiniteTimeout = 0xFFFFFFFF
syscallWatcherTimeout = 4 * time.Minute
)
// hresultErr converts an HRESULT return value into a Go error, unwrapping
// FACILITY_WIN32 the same way hcsshim's generated bindings do.
func hresultErr(r0 uintptr) error {
if int32(r0) >= 0 {
return nil
}
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
return syscall.Errno(r0)
}
// coString copies a CoTaskMem-allocated PWSTR into a Go string and frees it.
func coString(p *uint16) string {
if p == nil {
return ""
}
s := windows.UTF16PtrToString(p)
windows.CoTaskMemFree(unsafe.Pointer(p))
return s
}
func hcsCreateOperation() (hcsOperation, error) {
r0, _, e1 := syscall.SyscallN(procHcsCreateOperation.Addr(), 0, 0)
if r0 == 0 {
return 0, fmt.Errorf("HcsCreateOperation: %w", e1)
}
return hcsOperation(r0), nil
}
func hcsCloseOperation(op hcsOperation) {
_, _, _ = syscall.SyscallN(procHcsCloseOperation.Addr(), uintptr(op))
}
// hcsWait drives one async HCS call to completion and returns the result
// document (which carries structured error info on failure).
func hcsWait(op hcsOperation, what string, callErr error) (string, error) {
if callErr != nil {
return "", fmt.Errorf("%s: %w", what, callErr)
}
var result *uint16
r0, _, _ := syscall.SyscallN(procHcsWaitForOperationResult.Addr(),
uintptr(op), uintptr(infiniteTimeout), uintptr(unsafe.Pointer(&result)))
doc := coString(result)
if err := hresultErr(r0); err != nil {
return doc, fmt.Errorf("%s: %w (result: %s)", what, err, doc)
}
return doc, nil
}
func hcsCreateComputeSystem(id, configuration string) (hcsSystem, error) {
op, err := hcsCreateOperation()
if err != nil {
return 0, err
}
defer hcsCloseOperation(op)
idP, err := syscall.UTF16PtrFromString(id)
if err != nil {
return 0, err
}
cfgP, err := syscall.UTF16PtrFromString(configuration)
if err != nil {
return 0, err
}
var system hcsSystem
r0, _, _ := syscall.SyscallN(procHcsCreateComputeSystem.Addr(),
uintptr(unsafe.Pointer(idP)), uintptr(unsafe.Pointer(cfgP)),
uintptr(op), 0, uintptr(unsafe.Pointer(&system)))
if _, err := hcsWait(op, "HcsCreateComputeSystem", hresultErr(r0)); err != nil {
return 0, err
}
return system, nil
}
func hcsStartComputeSystem(system hcsSystem) error {
op, err := hcsCreateOperation()
if err != nil {
return err
}
defer hcsCloseOperation(op)
r0, _, _ := syscall.SyscallN(procHcsStartComputeSystem.Addr(),
uintptr(system), uintptr(op), 0)
_, err = hcsWait(op, "HcsStartComputeSystem", hresultErr(r0))
return err
}
func hcsTerminateComputeSystem(system hcsSystem) error {
op, err := hcsCreateOperation()
if err != nil {
return err
}
defer hcsCloseOperation(op)
r0, _, _ := syscall.SyscallN(procHcsTerminateComputeSystem.Addr(),
uintptr(system), uintptr(op), 0)
_, err = hcsWait(op, "HcsTerminateComputeSystem", hresultErr(r0))
return err
}
func execute(ctx context.Context, timeout time.Duration, f func() error) error {
done := make(chan error, 1)
go func() { done <- f() }()
var watcher <-chan time.Time
if timeout > 0 {
t := time.NewTimer(timeout)
defer t.Stop()
watcher = t.C
}
for {
select {
case err := <-done:
return err
case <-watcher:
logrus.WithField("timeout", timeout).
Warn("HCS syscall exceeded timeout; still waiting to avoid use-after-free in computecore.dll")
watcher = nil
case <-ctx.Done():
logrus.WithError(ctx.Err()).
Warn("HCS syscall context canceled; still waiting to avoid use-after-free in computecore.dll")
ctx = context.WithoutCancel(ctx)
}
}
}
func hcsGetComputeSystemProperties(ctx context.Context, system hcsSystem, operation hcsOperation, propertyQuery string) (hr error) {
return execute(ctx, syscallWatcherTimeout, func() error {
return _hcsGetComputeSystemProperties(system, operation, propertyQuery)
})
}
func _hcsGetComputeSystemProperties(system hcsSystem, operation hcsOperation, propertyQuery string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(propertyQuery)
if hr != nil {
return hr
}
hr = procHcsGetComputeSystemProperties.Find()
if hr != nil {
return hr
}
r0, _, _ := syscall.SyscallN(procHcsGetComputeSystemProperties.Addr(), uintptr(system), uintptr(operation), uintptr(unsafe.Pointer(_p0)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return hr
}
func hcsOpenComputeSystem(ctx context.Context, id string, requestedAccess uint32) (computeSystem hcsSystem, hr error) {
hr = execute(ctx, syscallWatcherTimeout, func() error {
return _hcsOpenComputeSystem(id, requestedAccess, &computeSystem)
})
return computeSystem, hr
}
func _hcsOpenComputeSystem(id string, requestedAccess uint32, computeSystem *hcsSystem) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(id)
if hr != nil {
return hr
}
hr = procHcsOpenComputeSystem.Find()
if hr != nil {
return hr
}
r0, _, _ := syscall.SyscallN(procHcsOpenComputeSystem.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(requestedAccess), uintptr(unsafe.Pointer(computeSystem)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return hr
}
func coTaskMemFree(buffer unsafe.Pointer) {
_, _, _ = syscall.SyscallN(procCoTaskMemFree.Addr(), uintptr(buffer))
}
func convertAndFreeCoTaskMemString(buffer *uint16) string {
str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:])
coTaskMemFree(unsafe.Pointer(buffer))
return str
}
func hcsWaitForComputeSystemExit(ctx context.Context, system hcsSystem, timeoutMs uint32) (result string, hr error) {
return result, execute(ctx, syscallWatcherTimeout, func() error {
var resultp *uint16
err := _hcsWaitForComputeSystemExit(system, timeoutMs, &resultp)
if resultp != nil {
result = convertAndFreeCoTaskMemString(resultp)
}
return err
})
}
func _hcsWaitForComputeSystemExit(system hcsSystem, timeoutMs uint32, result **uint16) (hr error) {
hr = procHcsWaitForComputeSystemExit.Find()
if hr != nil {
return hr
}
r0, _, _ := syscall.SyscallN(procHcsWaitForComputeSystemExit.Addr(), uintptr(system), uintptr(timeoutMs), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return hr
}
func hcsCloseComputeSystem(system hcsSystem) {
_, _, _ = syscall.SyscallN(procHcsCloseComputeSystem.Addr(), uintptr(system))
}
// hcsGrantVMAccess ACLs a host file so the VM worker process may read it
// (hcsshim does the equivalent before every kernel-direct boot).
func hcsGrantVMAccess(vmID, path string) error {
idP, err := syscall.UTF16PtrFromString(vmID)
if err != nil {
return err
}
pathP, err := syscall.UTF16PtrFromString(path)
if err != nil {
return err
}
r0, _, _ := syscall.SyscallN(procHcsGrantVMAccess.Addr(),
uintptr(unsafe.Pointer(idP)), uintptr(unsafe.Pointer(pathP)))
return hresultErr(r0)
}
+416
View File
@@ -0,0 +1,416 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"context"
"errors"
"fmt"
"net"
"os"
"path/filepath"
"time"
"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/hcn"
"github.com/docker/go-units"
"github.com/lima-vm/go-qcow2reader/image/vhdx"
"github.com/sirupsen/logrus"
"github.com/lima-vm/lima/v2/pkg/driver"
"github.com/lima-vm/lima/v2/pkg/limatype"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
"github.com/lima-vm/lima/v2/pkg/reflectutil"
)
var knownYamlProperties = []string{
"AdditionalDisks",
"Arch",
"Audio",
"CACertificates",
"Containerd",
"CopyToHost",
"CPUs",
"CPUType",
"Disk",
"DNS",
"Env",
"Firmware",
"GuestInstallPrefix",
"HostResolver",
"Images",
"Memory",
"Message",
"MinimumLimaVersion",
"Mounts",
"MountType",
"MountTypesUnsupported",
"MountInotify",
"NestedVirtualization",
"Networks",
"OS",
"Param",
"Plain",
"PortForwards",
"Probes",
"PropagateProxyEnv",
"Provision",
"SSH",
"TimeZone",
"TPM",
"UpgradePackages",
"User",
"Video",
"VMType",
"VMOpts",
}
const Enabled = true
type LimaHcsDriver struct {
Instance *limatype.Instance
SSHLocalPort int
vSockPort int
virtioPort string
system hcsSystem
serialListener net.Listener
sshAddress string
}
var _ driver.Driver = (*LimaHcsDriver)(nil)
func New() *LimaHcsDriver {
return &LimaHcsDriver{}
}
func (l *LimaHcsDriver) Configure(ctx context.Context, inst *limatype.Instance) (*driver.ConfiguredDriver, error) {
cfg := inst.Config
if cfg.VMType == nil {
cfg.VMType = new(limatype.HCS)
}
if cfg.MountType == nil {
cfg.MountType = new(limatype.REVSSHFS)
}
if err := validateConfig(ctx, cfg); err != nil {
return nil, err
}
l.Instance = inst
l.SSHLocalPort = inst.SSHLocalPort
return &driver.ConfiguredDriver{
Driver: l,
}, nil
}
func (l *LimaHcsDriver) Validate(ctx context.Context) error {
return validateConfig(ctx, l.Instance.Config)
}
func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
if cfg == nil {
return errors.New("configuration is nil")
}
// TODO: revise this list for HCS
if cfg.VMType != nil {
if unknown := reflectutil.UnknownNonEmptyFields(cfg, knownYamlProperties...); len(unknown) > 0 {
logrus.Warnf("Ignoring: vmType %s: %+v", *cfg.VMType, unknown)
}
}
if cfg.OS != nil && *cfg.OS == limatype.WINDOWS {
return errors.New("currently Windows guest OS is only supported on QEMU")
}
if !limatype.IsNativeArch(*cfg.Arch) {
return fmt.Errorf("unsupported arch: %#q", *cfg.Arch)
}
if cfg.TPM != nil && *cfg.TPM {
return errors.New("field `tpm` is not supported on HCS driver")
}
if cfg.VMType != nil {
if cfg.Mounts != nil {
for i, mount := range cfg.Mounts {
if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 {
logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *cfg.VMType, i, unknown)
}
}
}
if cfg.Networks != nil {
for i, network := range cfg.Networks {
if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 {
logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *cfg.VMType, i, unknown)
}
}
}
if cfg.Audio.Device != nil {
audioDevice := *cfg.Audio.Device
if audioDevice != "" {
logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *cfg.VMType, audioDevice)
}
}
if cfg.HostResolver.Enabled == nil {
cfg.HostResolver.Enabled = new(false)
}
if *cfg.HostResolver.Enabled {
return errors.New("the HCS driver does not support HostResolver.Enabled")
}
}
return nil
}
func (l *LimaHcsDriver) BootScripts(_ context.Context) (map[string][]byte, error) {
return nil, nil
}
func (l *LimaHcsDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string {
inst.SSHLocalPort = 22
state, err := getComputeSystemState(ctx, "lima-"+inst.Name)
if err != nil {
if errors.Is(err, errNotFound) {
inst.Status = limatype.StatusUnknown
return inst.Status
}
inst.Status = limatype.StatusBroken
inst.Errors = append(inst.Errors, fmt.Errorf("failed to inspect HCS sompute system: %w", err))
return inst.Status
}
if state == "Running" {
inst.Status = limatype.StatusRunning
e, err := hcn.GetEndpointByName(endpointName(inst.Name))
if err != nil {
logrus.Errorf("failed to get HCN endpoint: %s", err.Error())
inst.Status = limatype.StatusBroken
return inst.Status
}
ips := e.IpConfigurations
if len(ips) == 0 {
logrus.Errorf("the HCN endpoint for instance %#q does not have an IP address yet", inst.Name)
inst.Status = limatype.StatusBroken
return inst.Status
}
ip := ips[0]
l.sshAddress = ip.IpAddress
inst.SSHAddress = ip.IpAddress
} else {
inst.Status = limatype.StatusStopped
}
return inst.Status
}
func (l *LimaHcsDriver) Delete(_ context.Context) error {
if endpoint, err := hcn.GetEndpointByName(endpointName(l.Instance.Name)); err == nil {
if err := endpoint.Delete(); err != nil {
return fmt.Errorf("failed to delete the HCN endpoint: %w", err)
}
}
if network, err := hcn.GetNetworkByName(networkName(l.Instance.Name)); err == nil {
if err := network.Delete(); err != nil {
return fmt.Errorf("failed to delete the HCN network: %w", err)
}
}
return nil
}
func (l *LimaHcsDriver) Start(ctx context.Context) (chan error, error) {
inst := l.Instance
diskPath := filepath.Join(inst.Dir, filenames.Disk) + ".vhdx"
isoPath := filepath.Join(inst.Dir, filenames.CIDataISO)
network, err := ensureNetwork(networkName(inst.Name))
if err != nil {
return nil, err
}
endpoint, err := ensureEndpoint(network, endpointName(inst.Name), inst.Dir)
if err != nil {
return nil, err
}
pipeName := `\\.\pipe\lima-` + inst.Name + `-com1`
ln, err := winio.ListenPipe(pipeName, nil)
if err != nil {
return nil, fmt.Errorf("failed to listen on %#q: %w", pipeName, err)
}
l.serialListener = ln
go serveSerial(ln, filepath.Join(inst.Dir, filenames.SerialLog))
vmID := "lima-" + inst.Name
for _, p := range []string{diskPath, isoPath} {
if err := hcsGrantVMAccess(vmID, p); err != nil {
return nil, fmt.Errorf("failed to grant VM access to %#q: %w", p, err)
}
}
memBytes, err := units.RAMInBytes(*inst.Config.Memory)
if err != nil {
return nil, err
}
config, err := buildVMConfig(diskPath, isoPath, endpoint.Id, endpoint.MacAddress, pipeName, *inst.Config.CPUs, memBytes>>20)
if err != nil {
return nil, err
}
logrus.Debugf("HCS compute system config: %s", config)
system, err := createComputeSystem(ctx, "lima-"+inst.Name, config)
if err != nil {
return nil, err
}
l.system = system
if err := startComputeSystem(ctx, system); err != nil {
return nil, err
}
for i := range 50 {
endpoint, err = hcn.GetEndpointByName(endpointName(inst.Name))
if err != nil {
logrus.Warnf("failed in GetEndpointByName: %s", err.Error())
time.Sleep(2 * time.Second)
continue
}
if len(endpoint.IpConfigurations) == 0 {
if i == 49 {
return nil, fmt.Errorf("HNS did not assign an IP address to the endpoint %#q", endpoint.Name)
}
time.Sleep(2 * time.Second)
continue
}
l.sshAddress = endpoint.IpConfigurations[0].IpAddress
logrus.Infof("HCS: the guest is expected to get %s (MAC %s)", l.sshAddress, endpoint.MacAddress)
break
}
errCh := make(chan error, 1)
go func() {
result, waitErr := hcsWaitForComputeSystemExit(ctx, system, hcsTimeoutInfinite)
if waitErr != nil {
errCh <- fmt.Errorf("HCS compute system exited: %w (result=%s)", waitErr, result)
return
}
errCh <- nil
}()
return errCh, nil
}
func (l *LimaHcsDriver) RunGUI(_ context.Context) error {
return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "hcs", *l.Instance.Config.Video.Display)
}
func (l *LimaHcsDriver) Stop(ctx context.Context) error {
if l.system == 0 {
return nil
}
if err := terminateComputeSystem(ctx, l.system); err != nil {
return err
}
if l.serialListener != nil {
_ = l.serialListener.Close()
}
if endpoint, err := hcn.GetEndpointByName(endpointName(l.Instance.Name)); err == nil {
if err = endpoint.Delete(); err != nil {
return fmt.Errorf("failed to delete the HCN endpoint: %w", err)
}
}
if network, err := hcn.GetNetworkByName(networkName(l.Instance.Name)); err == nil {
if err := network.Delete(); err != nil {
return fmt.Errorf("failed to delete the HCN network %w", err)
}
}
return nil
}
// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
// As of 08-01-2024, github.com/mdlayher/vsock does not natively support vsock on
// Windows, so use the winio library to create the connection.
func (l *LimaHcsDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) {
return nil, "", nil
}
func (l *LimaHcsDriver) Info(_ context.Context) driver.Info {
var info driver.Info
info.Name = "hcs"
if l.Instance != nil {
info.InstanceDir = l.Instance.Dir
}
info.VirtioPort = l.virtioPort
info.VsockPort = l.vSockPort
info.Features = driver.DriverFeatures{
DynamicSSHAddress: true,
StaticSSHPort: true,
SkipSocketForwarding: true,
NoCloudInit: false,
CanRunGUI: false,
SupportedImageFormats: []string{string(vhdx.Type)},
}
return info
}
func (l *LimaHcsDriver) SSHAddress(_ context.Context) (string, error) {
if l.sshAddress == "" {
return "", errors.New("the SSH address is not known yet")
}
return l.sshAddress, nil
}
func (l *LimaHcsDriver) Create(_ context.Context) error {
return nil
}
func (l *LimaHcsDriver) CreateDisk(ctx context.Context) error {
if err := mayConvertQcow2ToVHDX(ctx, l.Instance.Dir); err != nil {
return err
}
disk := filepath.Join(l.Instance.Dir, filenames.Disk)
link := disk + ".vhdx"
if _, err := os.Stat(link); err == nil {
return nil
} else if !errors.Is(err, os.ErrNotExist) {
return err
}
return os.Link(disk, link)
}
func (l *LimaHcsDriver) ChangeDisplayPassword(_ context.Context, _ string) error {
return nil
}
func (l *LimaHcsDriver) DisplayConnection(_ context.Context) (string, error) {
return "", nil
}
func (l *LimaHcsDriver) CreateSnapshot(_ context.Context, _ string) error {
return errUnimplemented
}
func (l *LimaHcsDriver) ApplySnapshot(_ context.Context, _ string) error {
return errUnimplemented
}
func (l *LimaHcsDriver) DeleteSnapshot(_ context.Context, _ string) error {
return errUnimplemented
}
func (l *LimaHcsDriver) ListSnapshots(_ context.Context) (string, error) {
return "", errUnimplemented
}
func (l *LimaHcsDriver) ForwardGuestAgent(_ context.Context) bool {
// If driver is not providing, use host agent
return l.vSockPort == 0 && l.virtioPort == ""
}
func (l *LimaHcsDriver) AdditionalSetupForSSH(_ context.Context) error {
return nil
}
+244
View File
@@ -0,0 +1,244 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"os"
"path/filepath"
"reflect"
"runtime"
"testing"
"github.com/lima-vm/go-qcow2reader/image/vhdx"
"gotest.tools/v3/assert"
"github.com/lima-vm/lima/v2/pkg/limatype"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
)
func newTestInstance(t *testing.T, cfg *limatype.LimaYAML) *limatype.Instance {
t.Helper()
return &limatype.Instance{
Name: "hcs",
Dir: t.TempDir(),
SSHLocalPort: 60022,
Config: cfg,
}
}
func TestValidateConfig(t *testing.T) {
arch := limatype.NewArch(runtime.GOARCH)
tests := []struct {
name string
cfg *limatype.LimaYAML
expectedErr string
}{
{
name: "nil config is rejected",
cfg: nil,
expectedErr: "configuration is nil",
},
{
name: "minimal config is accepted",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
},
},
{
name: "Linux guest is accepted",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
OS: new(limatype.LINUX),
},
},
{
name: "Windows guest is rejected",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
OS: new(limatype.WINDOWS),
},
expectedErr: "currently Windows guest OS is only supported on QEMU",
},
{
name: "emulation of a non-native arch is rejected",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: new(limatype.Arch("unknown")),
},
expectedErr: "unsupported arch",
},
{
name: "tpm is rejected",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
TPM: new(true),
},
expectedErr: "field `tpm` is not supported on HCS driver",
},
{
name: "tpm=false is accepted",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
TPM: new(false),
},
},
{
name: "unsupported fields are only warned about, not rejected",
cfg: &limatype.LimaYAML{
VMType: new(limatype.HCS),
Arch: &arch,
Audio: limatype.Audio{Device: new("none")},
Mounts: []limatype.Mount{{Location: "~"}},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateConfig(t.Context(), tt.cfg)
if tt.expectedErr == "" {
assert.NilError(t, err)
} else {
assert.ErrorContains(t, err, tt.expectedErr)
}
})
}
}
func TestKnownYamlProperties(t *testing.T) {
yamlType := reflect.TypeFor[limatype.LimaYAML]()
for _, name := range knownYamlProperties {
t.Run(name, func(t *testing.T) {
_, ok := yamlType.FieldByName(name)
assert.Assert(t, ok, "limatype.LimaYAML has no field %#q", name)
})
}
}
func TestConfigure(t *testing.T) {
arch := limatype.NewArch(runtime.GOARCH)
t.Run("fills in the HCS defaults", func(t *testing.T) {
inst := newTestInstance(t, &limatype.LimaYAML{Arch: &arch})
l := New()
configured, err := l.Configure(t.Context(), inst)
assert.NilError(t, err)
assert.Assert(t, configured != nil)
assert.Equal(t, *inst.Config.VMType, limatype.HCS)
assert.Equal(t, *inst.Config.MountType, limatype.REVSSHFS)
assert.Equal(t, l.Instance, inst)
assert.Equal(t, l.SSHLocalPort, inst.SSHLocalPort)
})
t.Run("keeps the values that are already set", func(t *testing.T) {
inst := newTestInstance(t, &limatype.LimaYAML{
Arch: &arch,
VMType: new(limatype.HCS),
MountType: new(limatype.NINEP),
})
l := New()
_, err := l.Configure(t.Context(), inst)
assert.NilError(t, err)
assert.Equal(t, *inst.Config.MountType, limatype.NINEP)
})
t.Run("propagates a validation error", func(t *testing.T) {
inst := newTestInstance(t, &limatype.LimaYAML{
Arch: &arch,
TPM: new(true),
})
l := New()
configured, err := l.Configure(t.Context(), inst)
assert.ErrorContains(t, err, "field `tpm` is not supported")
assert.Assert(t, configured == nil)
})
}
func TestValidate(t *testing.T) {
arch := limatype.NewArch(runtime.GOARCH)
l := New()
l.Instance = newTestInstance(t, &limatype.LimaYAML{
Arch: &arch,
VMType: new(limatype.HCS),
})
assert.NilError(t, l.Validate(t.Context()))
l.Instance.Config.TPM = new(true)
assert.ErrorContains(t, l.Validate(t.Context()), "field `tpm` is not supported")
}
func TestInfo(t *testing.T) {
l := New()
info := l.Info(t.Context())
assert.Equal(t, info.Name, "hcs")
assert.Equal(t, info.InstanceDir, "")
assert.Equal(t, info.Features.NoCloudInit, false)
assert.Equal(t, info.Features.CanRunGUI, false)
assert.Equal(t, info.Features.DynamicSSHAddress, true)
assert.Equal(t, info.Features.StaticSSHPort, true)
assert.DeepEqual(t, info.Features.SupportedImageFormats, []string{string(vhdx.Type)})
l.Instance = newTestInstance(t, &limatype.LimaYAML{})
assert.Equal(t, l.Info(t.Context()).InstanceDir, l.Instance.Dir)
}
func TestSSHAddress(t *testing.T) {
l := New()
_, err := l.SSHAddress(t.Context())
assert.ErrorContains(t, err, "not known yet")
l.sshAddress = "192.168.10.5"
got, err := l.SSHAddress(t.Context())
assert.NilError(t, err)
assert.Equal(t, got, "192.168.10.5")
}
func TestCreateDisk(t *testing.T) {
l := New()
l.Instance = newTestInstance(t, &limatype.LimaYAML{})
diskPath := filepath.Join(l.Instance.Dir, filenames.Disk)
const content = "not a real disk"
assert.NilError(t, os.WriteFile(diskPath, []byte(content), 0o600))
assert.NilError(t, l.CreateDisk(t.Context()))
st, err := os.Stat(diskPath + ".vhdx")
assert.NilError(t, err)
assert.Equal(t, st.Size(), int64(len(content)))
assert.NilError(t, l.CreateDisk(t.Context()))
}
func TestGuestAgentIsForwardedByHostAgent(t *testing.T) {
l := New()
assert.Equal(t, l.ForwardGuestAgent(t.Context()), true)
conn, addr, err := l.GuestAgentConn(t.Context())
assert.NilError(t, err)
assert.Assert(t, conn == nil)
assert.Equal(t, addr, "")
}
func TestStopBeforeStart(t *testing.T) {
l := New()
l.Instance = newTestInstance(t, &limatype.LimaYAML{})
assert.NilError(t, l.Stop(t.Context()))
}
+325
View File
@@ -0,0 +1,325 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"github.com/Microsoft/hcsshim/hcn"
"github.com/lima-vm/go-qcow2reader/image/qcow2"
"github.com/lima-vm/go-qcow2reader/image/vhdx"
"github.com/sirupsen/logrus"
"github.com/lima-vm/lima/v2/pkg/imgutil/nativeimgutil"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
"github.com/lima-vm/lima/v2/pkg/limayaml"
)
const (
// hcsTimeoutInfinite corresponds to Win32 INFINITE.
hcsTimeoutInfinite uint32 = 0xFFFFFFFF
// These flags are not exported by the hcn package.
netFlagEnableDNS hcn.NetworkFlags = 1
netFlagEnableDHCP hcn.NetworkFlags = 2
netFlagIsolateSwitch hcn.NetworkFlags = 32
networkAddressPrefix = "192.168.10.0/24"
networkGateway = "192.168.10.1"
)
type computeSystemProperties struct {
State string `json:"State"`
}
var errNotFound = errors.New("not found")
// getComputeSystemState obtains compute system properties.
func getComputeSystemState(ctx context.Context, id string) (string, error) {
system, err := hcsOpenComputeSystem(ctx, id, syscall.GENERIC_ALL)
if err != nil {
return "", errNotFound
}
defer hcsCloseComputeSystem(system)
op, err := hcsCreateOperation()
if err != nil {
return "", fmt.Errorf("failed to create an HCS operation: %w", err)
}
defer hcsCloseOperation(op)
if err := hcsGetComputeSystemProperties(ctx, system, op, "{}"); err != nil {
return "", fmt.Errorf("failed to get compute system properties: %w", err)
}
result, err := hcsWait(op, "hcsGetComputeSystemProperties", nil)
if err != nil {
return "", fmt.Errorf("failed to get compute system properties: %w (result=%s)", err, result)
}
var properties computeSystemProperties
if err := json.Unmarshal([]byte(result), &properties); err != nil {
return "", fmt.Errorf("failed to decode compute system properties: %w", err)
}
return properties.State, nil
}
// buildVMConfig returns the HCS compute system configuration document.
func buildVMConfig(
diskPath, isoPath, endpointID, macAddress, serialPipe string,
cpus int, memoryMiB int64,
) (string, error) {
config := map[string]any{
"SchemaVersion": map[string]any{
"Major": 2,
"Minor": 1,
},
"Owner": "Lima",
"ShouldTerminateOnLastHandleClosed": true,
"VirtualMachine": map[string]any{
"Chipset": map[string]any{
"Uefi": map[string]any{
"Console": "ComPort1",
"BootThis": map[string]any{
"DevicePath": "Primary disk",
"DiskNumber": 0,
"DeviceType": "ScsiDrive",
},
},
},
"ComputeTopology": map[string]any{
"Memory": map[string]any{
"Backing": "Virtual",
"SizeInMB": memoryMiB,
},
"Processor": map[string]any{
"Count": cpus,
},
},
"Devices": map[string]any{
"Scsi": map[string]any{
"Primary disk": map[string]any{
"Attachments": map[string]any{
"0": map[string]any{
"Type": "VirtualDisk",
"Path": diskPath,
},
},
},
"cidata": map[string]any{
"Attachments": map[string]any{
"0": map[string]any{
"Type": "Iso",
"Path": isoPath,
},
},
},
},
"ComPorts": map[string]any{
"0": map[string]any{
"NamedPipe": serialPipe,
},
},
"NetworkAdapters": map[string]any{
endpointID: map[string]any{
"EndpointId": endpointID,
"MacAddress": macAddress,
},
},
},
},
}
configBytes, err := json.Marshal(config)
if err != nil {
return "", err
}
return string(configBytes), nil
}
func ensureNetwork(name string) (*hcn.HostComputeNetwork, error) {
network, err := hcn.GetNetworkByName(name)
if err == nil {
return network, nil
}
if !hcn.IsNotFoundError(err) {
return nil, fmt.Errorf("failed to look up the HCN network %#q: %w", name, err)
}
network = &hcn.HostComputeNetwork{
Name: name,
SchemaVersion: hcn.V2SchemaVersion(),
Type: hcn.ICS,
Flags: netFlagEnableDNS | netFlagEnableDHCP | hcn.EnableNonPersistent | netFlagIsolateSwitch,
Ipams: []hcn.Ipam{{
Type: "Static",
Subnets: []hcn.Subnet{{
IpAddressPrefix: networkAddressPrefix,
Routes: []hcn.Route{{
NextHop: networkGateway,
DestinationPrefix: "0.0.0.0/0",
}},
}},
}},
}
return network.Create()
}
func ensureEndpoint(network *hcn.HostComputeNetwork, name, instDir string) (*hcn.HostComputeEndpoint, error) {
endpoint, err := hcn.GetEndpointByName(name)
if err == nil {
if endpoint.HostComputeNetwork != network.Id {
return nil, fmt.Errorf("endpoint %#q already exists on a different network", name)
}
return endpoint, nil
}
if !hcn.IsNotFoundError(err) {
return nil, err
}
endpoint = &hcn.HostComputeEndpoint{
Name: name,
SchemaVersion: hcn.V2SchemaVersion(),
HostComputeNetwork: network.Id,
MacAddress: hnsMACAddress(instDir),
}
return endpoint.Create()
}
func networkName(instName string) string {
return "lima-" + instName + "-net"
}
func endpointName(instName string) string {
return "lima-" + instName + "-ep"
}
// hnsMACAddress converts MAC address notation saved in limayaml struct to the one HNS expects.
func hnsMACAddress(instDir string) string {
return strings.ToUpper(strings.ReplaceAll(limayaml.MACAddress(instDir), ":", "-"))
}
// createComputeSystem creates (but does not start) an HCS compute system.
func createComputeSystem(_ context.Context, id, config string) (hcsSystem, error) {
system, err := hcsCreateComputeSystem(id, config)
if err != nil {
return 0, fmt.Errorf("create compute system error: %w", err)
}
return system, nil
}
func startComputeSystem(_ context.Context, system hcsSystem) error {
return hcsStartComputeSystem(system)
}
func terminateComputeSystem(_ context.Context, system hcsSystem) error {
return hcsTerminateComputeSystem(system)
}
// serveSerial accepts connections from the guest port.
// It appends everything it reads to logPath and return when ln is closed.
func serveSerial(ln net.Listener, logPath string) {
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
logrus.WithError(err).Errorf("failed to open the serial log %#q", logPath)
return
}
defer f.Close()
for {
conn, err := ln.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
logrus.WithError(err).Warn("failed to accept a serial connection")
return
}
logrus.Debugf("serial: the guest connected to %s", ln.Addr())
if _, err := io.Copy(f, conn); err != nil {
logrus.WithError(err).Debug("serial: copy finished with an error")
}
conn.Close()
}
}
// mayConvertQcow2ToVHDX checks `disk` and converts it to .vhdx if it is qcow2.
func mayConvertQcow2ToVHDX(ctx context.Context, instDir string) error {
disk := filepath.Join(instDir, filenames.Disk)
_, err := os.Stat(disk)
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return err
}
imagePath := filepath.Join(instDir, filenames.Image)
format, err := nativeimgutil.DetectFormat(imagePath)
if err != nil {
return err
}
switch format {
case string(vhdx.Type):
logrus.Debug("disk is already .vhdx")
if err = os.Rename(imagePath, disk); err != nil {
return fmt.Errorf("failed to rename %#q to %#q: %w", imagePath, disk, err)
}
if err := os.Chmod(disk, 0o644); err != nil {
return fmt.Errorf("failed to chmod %#q: %w", disk, err)
}
return nil
case qcow2.Type, "raw":
if err := execQemuImgConvert(ctx, imagePath, disk); err != nil {
return fmt.Errorf("failed to convert to vhdx: %w", err)
}
if err := execFsutil(ctx, disk); err != nil {
return fmt.Errorf("failed to set disk flag: %w", err)
}
return nil
default:
return fmt.Errorf("disk type %s is not supported", format)
}
}
func execQemuImgConvert(ctx context.Context, source, dist string) error {
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, "qemu-img", "convert", "-O", "vhdx", source, dist)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run %v: stdout=%#q, stderr=%#q: %w",
cmd.Args, stdout.String(), stderr.String(), err)
}
return nil
}
func execFsutil(ctx context.Context, disk string) error {
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, "fsutil", "sparse", "setflag", disk, "0")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run %v: stdout=%#q, stderr=%#q: %w", cmd.Args, stdout.String(), stderr.String(), err)
}
return nil
}
+173
View File
@@ -0,0 +1,173 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"encoding/json"
"net"
"os"
"path/filepath"
"regexp"
"sync"
"testing"
"gotest.tools/v3/assert"
)
const (
testDiskPath = `C:\Users\lima\.lima\hcs\disk.vhdx`
testISOPath = `C:\Users\lima\.lima\hcs\cidata.iso`
testEndpointID = "11111111-2222-3333-4444-555555555555"
testMACAddress = "52-55-55-AA-BB-CC"
testSerialPipe = `\\.\pipe\lima-hcs-com1`
testCPUs = 2
testMemoryMiB = 2048
)
func TestNetworkAndEndpointName(t *testing.T) {
assert.Equal(t, networkName("test"), "lima-test-net")
assert.Equal(t, endpointName("test"), "lima-test-ep")
}
func TestHNSMACAddress(t *testing.T) {
dir := t.TempDir()
got := hnsMACAddress(dir)
re := regexp.MustCompile(`^52-55-55(-[0-9A-F]{2}){3}$`)
assert.Assert(t, re.MatchString(got), "unexpected MAC address %#q", got)
assert.Equal(t, got, hnsMACAddress(dir), "MAC Address should be identical if input is the same.")
assert.Assert(t, got != hnsMACAddress(t.TempDir()))
}
func TestBuildVMConfig(t *testing.T) {
got, err := buildVMConfig(testDiskPath, testISOPath, testEndpointID, testMACAddress, testSerialPipe, testCPUs, testMemoryMiB)
assert.NilError(t, err)
const want = `{
"SchemaVersion": {"Major": 2, "Minor": 1},
"Owner": "Lima",
"ShouldTerminateOnLastHandleClosed": true,
"VirtualMachine": {
"Chipset": {
"Uefi": {
"Console": "ComPort1",
"BootThis": {"DevicePath": "Primary disk", "DiskNumber": 0, "DeviceType": "ScsiDrive"}
}
},
"ComputeTopology": {
"Memory": {"Backing": "Virtual", "SizeInMB": 2048},
"Processor": {"Count": 2}
},
"Devices": {
"Scsi": {
"Primary disk": {
"Attachments": {"0": {"Type": "VirtualDisk", "Path": "C:\\Users\\lima\\.lima\\hcs\\disk.vhdx"}}
},
"cidata": {
"Attachments": {"0": {"Type": "Iso", "Path": "C:\\Users\\lima\\.lima\\hcs\\cidata.iso"}}
}
},
"ComPorts": {"0": {"NamedPipe": "\\\\.\\pipe\\lima-hcs-com1"}},
"NetworkAdapters": {
"11111111-2222-3333-4444-555555555555": {
"EndpointId": "11111111-2222-3333-4444-555555555555",
"MacAddress": "52-55-55-AA-BB-CC"
}
}
}
}
}`
var gotDoc, wantDoc any
assert.NilError(t, json.Unmarshal([]byte(got), &gotDoc))
assert.NilError(t, json.Unmarshal([]byte(want), &wantDoc))
assert.DeepEqual(t, gotDoc, wantDoc)
}
func TestBuildVMConfigSerialPortNumbering(t *testing.T) {
got, err := buildVMConfig(testDiskPath, testISOPath, testEndpointID, testMACAddress, testSerialPipe, testCPUs, testMemoryMiB)
assert.NilError(t, err)
var doc struct {
VirtualMachine struct {
Chipset struct {
Uefi struct {
Console string
}
}
Devices struct {
ComPorts map[string]struct {
NamedPipe string
}
}
}
}
assert.NilError(t, json.Unmarshal([]byte(got), &doc))
assert.Equal(t, doc.VirtualMachine.Chipset.Uefi.Console, "ComPort1")
assert.Equal(t, doc.VirtualMachine.Devices.ComPorts["0"].NamedPipe, testSerialPipe)
}
func TestServeSerial(t *testing.T) {
logPath := filepath.Join(t.TempDir(), "serial.log")
ln := newFakeListener()
done := make(chan struct{})
go func() {
serveSerial(ln, logPath)
close(done)
}()
lines := []string{"[ 0.000000] Linux version 6.11\n", "lima login: \n"}
for _, line := range lines {
guest, host := net.Pipe()
ln.conns <- host
_, err := guest.Write([]byte(line))
assert.NilError(t, err)
assert.NilError(t, guest.Close())
}
assert.NilError(t, ln.Close())
<-done
b, err := os.ReadFile(logPath)
assert.NilError(t, err)
assert.Equal(t, string(b), lines[0]+lines[1])
}
type fakeListener struct {
conns chan net.Conn
done chan struct{}
once sync.Once
}
func newFakeListener() *fakeListener {
return &fakeListener{
conns: make(chan net.Conn),
done: make(chan struct{}),
}
}
func (ln *fakeListener) Accept() (net.Conn, error) {
select {
case conn := <-ln.conns:
return conn, nil
case <-ln.done:
return nil, net.ErrClosed
}
}
func (ln *fakeListener) Close() error {
ln.once.Do(func() { close(ln.done) })
return nil
}
func (ln *fakeListener) Addr() net.Addr { return fakeAddr{} }
type fakeAddr struct{}
func (fakeAddr) Network() string { return "fake" }
func (fakeAddr) String() string { return "fake" }
+16
View File
@@ -0,0 +1,16 @@
//go:build windows && !external_hcs
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package hcs
import (
"context"
"github.com/lima-vm/lima/v2/pkg/registry"
)
func init() {
registry.Register(context.Background(), New())
}
+2 -2
View File
@@ -155,7 +155,7 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o
}
var udpDNSLocalPort, tcpDNSLocalPort int
if *inst.Config.HostResolver.Enabled {
if limayaml.HostResolverEnabled(inst.Config) {
udpDNSLocalPort, err = freeport.UDP()
if err != nil {
return nil, err
@@ -408,7 +408,7 @@ func (a *HostAgent) Run(ctx context.Context) error {
}()
adjustNofileRlimit()
if limayaml.FirstUsernetIndex(a.instConfig) == -1 && *a.instConfig.HostResolver.Enabled {
if limayaml.FirstUsernetIndex(a.instConfig) == -1 && limayaml.HostResolverEnabled(a.instConfig) {
hosts := a.instConfig.HostResolver.Hosts
if hosts == nil {
hosts = make(map[string]string)
+6
View File
@@ -17,6 +17,7 @@ import (
"github.com/docker/go-units"
"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/go-qcow2reader/image/vhdx"
"github.com/sirupsen/logrus"
"github.com/lima-vm/lima/v2/pkg/autostart"
@@ -537,6 +538,11 @@ func prepareDisk(ctx context.Context, inst *limatype.Instance) error {
diskSize := img.Size()
if img.Type() == vhdx.Type {
logrus.Warn("Skip resizing the instance because go-qcow2reader cannot read vhdx type image")
return nil
}
if inst.Disk == diskSize {
return nil
}
+2 -1
View File
@@ -99,13 +99,14 @@ const (
QEMU VMType = "qemu"
VZ VMType = "vz"
WSL2 VMType = "wsl2"
HCS VMType = "hcs"
)
var (
OSTypes = []OS{LINUX, DARWIN, FREEBSD, WINDOWS}
ArchTypes = []Arch{X8664, AARCH64, ARMV7L, PPC64LE, RISCV64, S390X}
MountTypes = []MountType{REVSSHFS, NINEP, VIRTIOFS, WSLMount}
VMTypes = []VMType{QEMU, VZ, WSL2}
VMTypes = []VMType{QEMU, VZ, WSL2, HCS}
)
type User struct {
+9 -3
View File
@@ -78,6 +78,14 @@ func FirstUsernetIndex(l *limatype.LimaYAML) int {
return slices.IndexFunc(l.Networks, func(network limatype.Network) bool { return networks.IsUsernet(network.Lima) })
}
// HostResolverEnabled returns whether the host resolver is enabled.
// The default value depends on the driver and is filled in the driver-specific FillDefault().
// A nil value is treated as true to keep HostResolver.Enabled true for drivers which do not fill the field.
func HostResolverEnabled(l *limatype.LimaYAML) bool {
return l.HostResolver.Enabled == nil || *l.HostResolver.Enabled
}
func MACAddress(uniqueID string) string {
sha := sha256.Sum256([]byte(osutil.MachineID() + uniqueID))
// "5" is the magic number in the Lima ecosystem.
@@ -616,9 +624,7 @@ func FillDefault(ctx context.Context, y, d, o *limatype.LimaYAML, filePath strin
if o.HostResolver.Enabled != nil {
y.HostResolver.Enabled = o.HostResolver.Enabled
}
if y.HostResolver.Enabled == nil {
y.HostResolver.Enabled = new(true)
}
// y.HostResolver.Enabled default value depends on the driver; filled in driver-specific FillDefault()
if y.HostResolver.IPv6 == nil {
y.HostResolver.IPv6 = d.HostResolver.IPv6
+3 -2
View File
@@ -140,7 +140,7 @@ func TestFillDefault(t *testing.T) {
Display: new("none"),
},
HostResolver: limatype.HostResolver{
Enabled: new(true),
Enabled: nil,
IPv6: new(false),
},
PropagateProxyEnv: new(true),
@@ -539,7 +539,8 @@ func TestFillDefault(t *testing.T) {
expect.HostResolver.Hosts["default"] = dExpected.HostResolver.Hosts["default"]
// dExpected.DNS will be ignored, and not appended to y.DNS
// The user-provided default is applied when the config doesn't specify it.
expect.HostResolver.Enabled = dExpected.HostResolver.Enabled
// "TWO" does not exist in filledDefaults.Env, so is set from dExpected.Env
expect.Env["TWO"] = dExpected.Env["TWO"]
+8 -2
View File
@@ -401,8 +401,14 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
}
}
if y.HostResolver.Enabled != nil && *y.HostResolver.Enabled && len(y.DNS) > 0 {
errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true"))
if y.VMType != nil && *y.VMType == limatype.HCS {
if y.HostResolver.Enabled != nil && *y.HostResolver.Enabled && len(y.DNS) > 0 {
errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true"))
}
} else {
if HostResolverEnabled(y) && len(y.DNS) > 0 {
errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true"))
}
}
err := validateNetwork(y)
+1
View File
@@ -122,6 +122,7 @@ See <https://lima-vm.io/docs/examples/ai/>.
- [`experimental/vnc`](./experimental/vnc.yaml): use vnc display and xorg server
- [`experimental/alsa`](./experimental/alsa.yaml): use alsa and default audio device
- [`experimental/hcs`](./experimental/hcs.yaml): use [HCS driver](https://lima-vm.io/docs/config/vmtype/hcs/) on Windows hosts
### Lost+found
+1 -1
View File
@@ -614,7 +614,7 @@ propagateProxyEnv: null
# can only work when using a vmnet network interface and the host has working
# IPv6 configured as well.
hostResolver:
# 🟢 Builtin default: true
# 🟢 Builtin default: true (except for hcs driver; it should be always false with hcs driver)
enabled: null
# 🟢 Builtin default: false
ipv6: null
+13
View File
@@ -0,0 +1,13 @@
# This template only works on Windows versions with Hyper-V platform enabled.
vmType: hcs
images:
# The following image is qcow2; the hcs driver converts it into VHDX on the first start using `qemu-img`.
- location: "https://cloud-images.ubuntu.com/releases/resolute/release-20260720/ubuntu-26.04-server-cloudimg-amd64.img"
arch: "x86_64"
digest: "sha256:117816726abbdefc5ef3e38902e81a76f1c76c3610e709999d0885f9d5d9b477"
plain: true
dns:
- 8.8.8.8
@@ -0,0 +1,66 @@
---
title: HCS
weight: 5
---
> **Warning**
> "hcs" mode is experimental
| ⚡ Requirement | Lima >= 2.3 + (Windows >= Windows 11) |
| ----------------- | -------------------------------------------------------- |
"hcs" option makes use of native virtualization support provided by Windows' Host Compute System (HCS) API ([more info](https://learn.microsoft.com/en-us/virtualization/api/hcs/overview)).
An example configuration:
{{< tabpane text=true >}}
{{% tab header="CLI" %}}
```bash
limactl start --vm-type=hcs --plain --dns 8.8.8.8
```
{{% /tab %}}
{{% tab header="YAML" %}}
```yaml
# Example to run Ubuntu using vmType: hcs
vmType: hcs
images:
- location: "https://cloud-images.ubuntu.com/releases/resolute/release-20260720/ubuntu-26.04-server-cloudimg-amd64.img"
arch: "x86_64"
digest: "sha256:117816726abbdefc5ef3e38902e81a76f1c76c3610e709999d0885f9d5d9b477"
plain: true
dns:
- 8.8.8.8
```
{{% /tab %}}
{{< /tabpane >}}
### Caveats
- We tested it works only on Windows 11 (x86_64).
- Only plain mode is supported (no file mount, no dynamic port-forwarding).
- `qemu-img` is required for the image conversion; see [Disk images](#disk-images).
- The user must be a member of the Hyper-V Administrators group; see [Setup for HCS driver](#setup-for-hcs-driver)
- `limactl` should be run from a shell opened with Administrator privileges.
- Currently, only one instance can run using hcs at a time, beucause the HCN network subnet is fixed and cannot be shared by multiple instances.
- A DNS server has to be specified explicitly.
### Disk images
- The `hcs` driver can only boot from a `.vhdx` disk image.
- If the image is not a `.vhdx` (for example `.qcow2`), Lima automatically converts it to `.vhdx` by running `qemu-img convert -O vhdx`.
- Therefore, if you would like to download the image or to pass a disk image which is not `.vhdx`, `qemu-img` is required.
### Setup for HCS driver
1. **Enable virtualization on Windows:**
Follow the instrunctions in [Microsoft guide](https://support.microsoft.com/en-us/windows/experience/enable-virtualization-on-windows) to enable virtualization on your Windows host.
2. **Add yourself to `Hyper-V Administrators` group:**
Run the following command on powershell or cmd.exe with Run as administrator:
```bash
net localgroup "Hyper-V Administrators" <user name> /add
```
Then, restart the terminal or reboot the computer for the changes to take effect.
To verify that your user account has been added to `Hyper-V Administrators` group, run:
```bash
net localgroup "Hyper-V Administrators"
```
+2 -1
View File
@@ -47,6 +47,7 @@ disk:
- `image`: the downloaded VM image; renamed to `disk` or `iso` during setup
- `image.ipsw`: hardlink to `image`, created for running `VZMacOSInstaller` that requires the image file to have the `.ipsw` suffix
- `disk`: the VM disk (can be a symlink to legacy `diffdisk`)
- `disk.vhdx`: hardlink to `disk` to pass a `.vhdx` path to HCS driver
- `iso`: optional CDROM image for ISO-based installations (can be a symlink to legacy `basedisk`)
- `basedisk`: legacy name for the downloaded image (pre-v2.1 instances; may remain as a qcow2 backing file)
- `diffdisk`: legacy name for `disk` (pre-v2.1 instances)
@@ -72,7 +73,7 @@ VZ:
- `vz-efi`: EFIVariable store file for a VM
Serial:
- `serial.log`: default serial log (QEMU only), for debugging
- `serial.log`: default serial log (QEMU and HCS), for debugging
- `serial.sock`: default serial socket (QEMU only), for debugging (Usage: `socat -,echo=0,icanon=0 unix-connect:serial.sock`)
- `serialp.log`: PCI serial log (QEMU (ARM) only), for debugging
- `serialp.sock`: PCI serial socket (QEMU (ARM) only), for debugging (Usage: `socat -,echo=0,icanon=0 unix-connect:serialp.sock`)
@@ -16,6 +16,7 @@ The following features are experimental and subject to change:
- `user.passwordlessSudo` (See [Sudo](../config/sudo))
- `External drivers`: building and using drivers as separate executables (see [Virtual Machine Drivers](../dev/drivers))
- [`vmType: krunkit`](../config/vmtype/krunkit.md)
- [`vmType:hcs`]((../config/vmtype/hcs.md))
- [`github` URL scheme](../templates/github.md): referencing templates on GitHub with `github:` URLs
- [macOS guests](../usage/guests/macos.md)
- [FreeBSD guests](../usage/guests/freebsd.md)