mirror of
https://github.com/lima-vm/lima.git
synced 2026-10-28 11:44:35 +08:00
27 files changed
+1700
-23
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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.
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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" }
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user