initial commit
Signed-off-by: Christine Dodrill <me@christine.website>
This commit is contained in:
commit
a735e4f67e
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2021 Christine Dodrill <me@christine.website>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,16 @@
|
|||
# How to generate names.json
|
||||
|
||||
Open https://xenoblade.github.io/xb2/bdat/common/BLD_NameList.html and paste
|
||||
this into the browser inspector:
|
||||
|
||||
```js
|
||||
let names = [];
|
||||
Array.from(document.getElementsByClassName("sortable")[0].children[1].children)
|
||||
.forEach(row => names.push(row.children[2]
|
||||
.innerHTML
|
||||
.toLowerCase()
|
||||
.replaceAll(" ", "-")));
|
||||
console.log(JSON.stringify(names));
|
||||
```
|
||||
|
||||
Then format it with jq.
|
|
@ -0,0 +1,131 @@
|
|||
let Distro =
|
||||
{ Type =
|
||||
{ name : Text
|
||||
, downloadURL : Text
|
||||
, sha256Sum : Text
|
||||
, minSize : Natural
|
||||
}
|
||||
, default = { name = "", downloadURL = "", sha256Sum = "", minSize = 5 }
|
||||
}
|
||||
|
||||
in [ Distro::{
|
||||
, name = "alpine-edge"
|
||||
, downloadURL =
|
||||
"https://xena.greedo.xeserv.us/pkg/alpine/img/alpine-edge-2021-05-15-cloud-init-within.qcow2"
|
||||
, sha256Sum =
|
||||
"c0ed716b9bd3dd45959496af4177935b0c491153c41d5d5e33eaf132bcc130c6"
|
||||
, minSize = 10
|
||||
}
|
||||
, Distro::{
|
||||
, name = "amazon-linux"
|
||||
, downloadURL =
|
||||
"https://cdn.amazonlinux.com/os-images/2.0.20210427.0/kvm/amzn2-kvm-2.0.20210427.0-x86_64.xfs.gpt.qcow2"
|
||||
, sha256Sum =
|
||||
"6ef9daef32cec69b2d0088626ec96410cd24afc504d57278bbf2f2ba2b7e529b"
|
||||
, minSize = 25
|
||||
}
|
||||
, Distro::{
|
||||
, name = "arch"
|
||||
, downloadURL =
|
||||
"https://mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg-20210515.22945.qcow2"
|
||||
, sha256Sum =
|
||||
"e4077f5ba3c5d545478f64834bc4852f9f7a2e05950fce8ecd0df84193162a27"
|
||||
, minSize = 2
|
||||
}
|
||||
, Distro::{
|
||||
, name = "centos-7"
|
||||
, downloadURL =
|
||||
"https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2"
|
||||
, sha256Sum =
|
||||
"1db30c9c272fb37b00111b93dcebff16c278384755bdbe158559e9c240b73b80"
|
||||
, minSize = 8
|
||||
}
|
||||
, Distro::{
|
||||
, name = "centos-8"
|
||||
, downloadURL =
|
||||
"https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.3.2011-20201204.2.x86_64.qcow2"
|
||||
, sha256Sum =
|
||||
"7ec97062618dc0a7ebf211864abf63629da1f325578868579ee70c495bed3ba0"
|
||||
, minSize = 10
|
||||
}
|
||||
, Distro::{
|
||||
, name = "debian-9"
|
||||
, downloadURL =
|
||||
"https://cdimage.debian.org/cdimage/openstack/9.13.21-20210511/debian-9.13.21-20210511-openstack-amd64.qcow2"
|
||||
, sha256Sum =
|
||||
"0667a08e2d947b331aee068db4bbf3a703e03edaf5afa52e23d534adff44b62a"
|
||||
, minSize = 2
|
||||
}
|
||||
, Distro::{
|
||||
, name = "debian-10"
|
||||
, downloadURL =
|
||||
"https://cdimage.debian.org/images/cloud/buster/20210329-591/debian-10-generic-amd64-20210329-591.qcow2"
|
||||
, sha256Sum =
|
||||
"70c61956095870c4082103d1a7a1cb5925293f8405fc6cb348588ec97e8611b0"
|
||||
, minSize = 2
|
||||
}
|
||||
, Distro::{
|
||||
, name = "fedora-34"
|
||||
, downloadURL =
|
||||
"https://download.fedoraproject.org/pub/fedora/linux/releases/34/Cloud/x86_64/images/Fedora-Cloud-Base-34-1.2.x86_64.qcow2"
|
||||
, sha256Sum =
|
||||
"b9b621b26725ba95442d9a56cbaa054784e0779a9522ec6eafff07c6e6f717ea"
|
||||
, minSize = 5
|
||||
}
|
||||
, Distro::{
|
||||
, name = "opensuse-leap-15.1"
|
||||
, downloadURL =
|
||||
"https://download.opensuse.org/repositories/Cloud:/Images:/Leap_15.2/images/openSUSE-Leap-15.2-OpenStack.x86_64.qcow2"
|
||||
, sha256Sum =
|
||||
"3203e256dab5981ca3301408574b63bc522a69972fbe9850b65b54ff44a96e0a"
|
||||
, minSize = 10
|
||||
}
|
||||
, Distro::{
|
||||
, name = "opensuse-leap-15.2"
|
||||
, downloadURL =
|
||||
"https://download.opensuse.org/repositories/Cloud:/Images:/Leap_15.2/images/openSUSE-Leap-15.2.x86_64-NoCloud.qcow2"
|
||||
, sha256Sum =
|
||||
"bd3c251ca52f9cf2ee0820258d75fd6d71502447eb0c7ae2592dc9a83ad7a0a1"
|
||||
, minSize = 10
|
||||
}
|
||||
, Distro::{
|
||||
, name = "ubuntu-16.04"
|
||||
, downloadURL =
|
||||
"https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
|
||||
, sha256Sum =
|
||||
"50a21bc067c05e0c73bf5d8727ab61152340d93073b3dc32eff18b626f7d813b"
|
||||
, minSize = 5
|
||||
}
|
||||
, Distro::{
|
||||
, name = "ubuntu-18.04"
|
||||
, downloadURL =
|
||||
"https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
|
||||
, sha256Sum =
|
||||
"bea55c09dde0d5c2dbac8a73c2ce4b93061264ba9c354d67939ae0e259d32906"
|
||||
, minSize = 5
|
||||
}
|
||||
, Distro::{
|
||||
, name = "ubuntu-20.04"
|
||||
, downloadURL =
|
||||
"https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
|
||||
, sha256Sum =
|
||||
"55e1feee6cbc5fed33505f04dbc2d06124ea06998599e5d3f7a2609b54b439c5"
|
||||
, minSize = 5
|
||||
}
|
||||
, Distro::{
|
||||
, name = "ubuntu-20.10"
|
||||
, downloadURL =
|
||||
"https://cloud-images.ubuntu.com/groovy/current/groovy-server-cloudimg-amd64.img"
|
||||
, sha256Sum =
|
||||
"c1332c24557389a129ff98fa169e34cb53c02555ed702a235e26b8978dd004c3"
|
||||
, minSize = 5
|
||||
}
|
||||
, Distro::{
|
||||
, name = "ubuntu-21.04"
|
||||
, downloadURL =
|
||||
"https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-amd64.img"
|
||||
, sha256Sum =
|
||||
"2f8a562637340a026f712594f1257673543d74725d8e3daf88d533d7b8bf448f"
|
||||
, minSize = 5
|
||||
}
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
["aizen","akatsuki","akebono","azai","arai","arufumi","ikazuchi","ikaruga","izayoi","izumo","ichiro","ikaku","ikki","issen","inazuma","ushio","oryuu","owashi","okina","oboro","orochi","kaiden","kaibyaku","karkan","kagemitsu","kagero","kazan","katsumasa","kanesada","kanehira","kanemitsu","kei","kijin","kibitsu","gokuto","kirim","gingar","kur","kuzan","kusanagi","kurochi","krogane","genno","kouki","kouru","kogarashi","kokras","gogyo","kojiro","kosor","kotetsu","kongir","konjiki","sakon","sangar","shun","shikiso","shishi","shichisei","shiko","shippun","shiden","shura","shungen","shin-mei","shinra","jin-rai","jinryuu","suro","sulgar","seigai","sysor","seiten","seimei","zeku","zeno","sordai","soten","sohmei","shouryu","sohaya","taiga","daiko","tyzan","taisei","tyhei","tadar","tsurugi","tenka","tenku","denko","toshi","tokka","hagan","hakusui","hakuto","hayate","hayabusa","hanni","hei","hiken","bizen","hynk","hideh","hiden","bakuya","huga","hiryu","fuwei","fugetsu","hukut","fudor","hekireki","bengara","horoh","hokuto","shigan","mikazuchi","mizuchi","mitsukage","mior","mu","mugen","musashi","mujo","musou","muchika","murakumo","murasame","meikyo","mejiro","yago","yakumo","yasha","yata","yanagi","yamato","yuki","yuzen","yuso","yumo","yoshikiri","rykiri","ranmaru","rikuzen","ryusei","ryo","rogen","reiki","roho","ai","aui","auba","akana","yoiyami","asagi","asai","aska","azuki","atori","amanei","amayori","ayame","anzu","koyuki","kyoka","isuzu","ichiku","iroha","uzura","uzuki","umi","ema","orka","kaeda","sarasa","kanami","kanon","karin","karei","karyn","kanna","kiko","kisaragi","kiri","kinsei","quina","kuko","kurumi","kurenai","kogoku","kokutan","kokuyo","kokoro","konoha","kohana","kohaku","sakuya","sazami","satsuki","sango","shiori","shigura","shisui","shizuku","shinome","shinobu","shimoki","shussu","shuraya","shiranui","shirayuki","shirayuri","shirome","suiren","suzu","suzukaze","suzuna","suzuran","sumira","tsumugi","sekirei","setsuka","sora","tamayori","chigusa","chidori","tsugumi","tsukumi","zutsuji","tsubaki","tsumi","tsura","tsuruba","tomae","torwa","nazuna","natsuki","nadeshiko","natori","ne-ne","nenoh","neyuki","nosuri","hasu","hazuki","hatsuharu","hatsuyuki","haruka","harusa","haruna","higana","hisui","hinagi","hinagetsu","hinata","hibari","hibiki","himawari","faera","fubuki","fuyoshi","yuzu","botania","honoka","madoka","mikagami","mikazuki","mika","mikoto","misaki","midori","minazuki","minami","minori","miyuki","mirei","mutsuki","maegi","mochi","momiji","moyoi","yayoi","yuka","yutsuji","yuna","yukina","yukine","yuzuki","yura","yuri","yomogi","rania","rinnia","lindora","lin","ruri","reika","rengenne","wakaba","utsuwaka","kai","kukir","kuro","goemon","koske","kotar","goro","kontro","sasuke","shimaru","shiro","tamar","tamon","tibbi","chamaru","tokoto","totetsu","baku","hutar","pochi","bonten","ryta","riku"]
|
|
@ -0,0 +1,10 @@
|
|||
module github.com/Xe/mkvm
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210513152157-a898af65b5cc
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/philandstuff/dhall-golang/v5 v5.0.0
|
||||
golang.org/x/tools v0.1.1
|
||||
)
|
|
@ -0,0 +1,76 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210513152157-a898af65b5cc h1:3KZmBt2nf5+YmSECo+RBnUzP7uCenf/Ckbs5wXLhHXY=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210513152157-a898af65b5cc/go.mod h1:o129ljs6alsIQTc8d6eweihqpmmrbxZ2g1jhgjhPykI=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f h1:lvGFo/tDOSQ4FKu0d2694s8XyOfAL6FLR9DCD5BIUW4=
|
||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406 h1:+OUpk+IVvmKU0jivOVFGtOzA6U5AWFs8HE4DRzWLOUE=
|
||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/philandstuff/dhall-golang/v5 v5.0.0 h1:v2k3t7shNw2lzvLTaJ7BN181SD/KsvqmJOQ9L//iBR4=
|
||||
github.com/philandstuff/dhall-golang/v5 v5.0.0/go.mod h1:rQY4dIWweuKdnU1VQ7jmpiF6uMd/FYhvfz883gDD53w=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/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 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,301 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/digitalocean/go-libvirt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/philandstuff/dhall-golang/v5"
|
||||
"golang.org/x/tools/txtar"
|
||||
)
|
||||
|
||||
//go:embed data/* templates/*
|
||||
var data embed.FS
|
||||
|
||||
var (
|
||||
distro = flag.String("distro", "alpine-edge", "the linux distro to install in the VM")
|
||||
name = flag.String("name", "", "the name of the VM, defaults to a random common blade name")
|
||||
zvolPrefix = flag.String("zvol-prefix", "rpool/mkvm-test/", "the prefix to use for zvol names")
|
||||
zvolSize = flag.Int("zvol-size", 0, "the number of gigabytes for the virtual machine disk")
|
||||
memory = flag.Int("memory", 512, "the number of megabytes of ram for the virtual machine")
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
flag.Parse()
|
||||
|
||||
if *name == "" {
|
||||
commonBladeName, err := getName()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
name = &commonBladeName
|
||||
}
|
||||
|
||||
distros, err := getDistros()
|
||||
if err != nil {
|
||||
log.Fatalf("can't load internal list of distros: %v", err)
|
||||
}
|
||||
|
||||
var resultDistro Distro
|
||||
var found bool
|
||||
for _, d := range distros {
|
||||
if d.Name == *distro {
|
||||
found = true
|
||||
resultDistro = d
|
||||
if *zvolSize == 0 {
|
||||
zvolSize = &d.MinSize
|
||||
}
|
||||
if *zvolSize < d.MinSize {
|
||||
zvolSize = &d.MinSize
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Printf("can't find distro %s in my list. Here are distros I know about:\n", *distro)
|
||||
for _, d := range distros {
|
||||
fmt.Println(d.Name)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
zvol := filepath.Join(*zvolPrefix, *name)
|
||||
|
||||
macAddress, err := randomMac()
|
||||
if err != nil {
|
||||
log.Fatalf("can't generate mac address: %v", err)
|
||||
}
|
||||
|
||||
l, err := connectToLibvirt()
|
||||
if err != nil {
|
||||
log.Fatalf("can't connect to libvirt: %v", err)
|
||||
}
|
||||
|
||||
log.Println("plan:")
|
||||
log.Printf("name: %s", *name)
|
||||
log.Printf("zvol: %s (%d GB)", zvol, *zvolSize)
|
||||
log.Printf("base image url: %s", resultDistro.DownloadURL)
|
||||
log.Printf("mac address: %s", macAddress)
|
||||
log.Printf("ram: %d MB", *memory)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("press enter if this looks okay:")
|
||||
reader.ReadString('\n')
|
||||
|
||||
cdir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Fatalf("can't find cache dir: %v", err)
|
||||
}
|
||||
cdir = filepath.Join(cdir, "within", "mkvm")
|
||||
os.MkdirAll(filepath.Join(cdir, "qcow2"), 0755)
|
||||
os.MkdirAll(filepath.Join(cdir, "seed"), 0755)
|
||||
qcowPath := filepath.Join(cdir, "qcow2", resultDistro.Sha256Sum)
|
||||
_, err = os.Stat(qcowPath)
|
||||
if err != nil {
|
||||
log.Printf("downloading distro image %s to %s", resultDistro.DownloadURL, qcowPath)
|
||||
fout, err := os.Create(qcowPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
resp, err := http.Get(resultDistro.DownloadURL)
|
||||
if err != nil {
|
||||
log.Fatalf("can't fetch qcow2 for %s (%s): %v", resultDistro.Name, resultDistro.DownloadURL, err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Fatalf("%s replied %s", resultDistro.DownloadURL, resp.Status)
|
||||
}
|
||||
|
||||
_, err = io.Copy(fout, resp.Body)
|
||||
if err != nil {
|
||||
log.Fatalf("download of %s failed: %v", resultDistro.DownloadURL, err)
|
||||
}
|
||||
|
||||
fout.Close()
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.ParseFS(data, "templates/*"))
|
||||
var buf = bytes.NewBuffer(nil)
|
||||
err = tmpl.ExecuteTemplate(buf, "cloud-config.txtar", struct{ Name string }{Name: *name})
|
||||
if err != nil {
|
||||
log.Fatalf("can't generate cloud-config: %v", err)
|
||||
}
|
||||
|
||||
arc := txtar.Parse(buf.Bytes())
|
||||
dir, err := os.MkdirTemp("", "mkvm")
|
||||
if err != nil {
|
||||
log.Fatalf("can't make directory: %v", err)
|
||||
}
|
||||
|
||||
for _, file := range arc.Files {
|
||||
fout, err := os.Create(filepath.Join(dir, file.Name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = fout.Write(file.Data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
isoPath := filepath.Join(cdir, "seed", fmt.Sprintf("%s-%s.iso", *name, resultDistro.Name))
|
||||
|
||||
err = run(
|
||||
"genisoimage",
|
||||
"-output",
|
||||
isoPath,
|
||||
"-volid",
|
||||
"cidata",
|
||||
"-joliet",
|
||||
"-rock",
|
||||
filepath.Join(dir, "meta-data"),
|
||||
filepath.Join(dir, "user-data"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ram := *memory * 1024
|
||||
vmID := uuid.New().String()
|
||||
buf.Reset()
|
||||
|
||||
// zfs create -V 20G rpool/safe/vm/sena
|
||||
err = run("sudo", "zfs", "create", "-V", fmt.Sprintf("%dG", *zvolSize), zvol)
|
||||
if err != nil {
|
||||
log.Fatalf("can't create zvol %s: %v", zvol, err)
|
||||
}
|
||||
|
||||
err = run("sudo", "qemu-img", "convert", "-O", "raw", qcowPath, filepath.Join("/dev/zvol", zvol))
|
||||
if err != nil {
|
||||
log.Fatalf("can't import qcow2: %v", err)
|
||||
}
|
||||
|
||||
err = tmpl.ExecuteTemplate(buf, "base.xml", struct {
|
||||
Name string
|
||||
UUID string
|
||||
Memory int
|
||||
ZVol string
|
||||
Seed string
|
||||
MACAddress string
|
||||
}{
|
||||
Name: *name,
|
||||
UUID: vmID,
|
||||
Memory: ram,
|
||||
ZVol: zvol,
|
||||
Seed: isoPath,
|
||||
MACAddress: macAddress,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("can't generate VM template: %v", err)
|
||||
}
|
||||
|
||||
domain, err := mkVM(l, buf)
|
||||
if err != nil {
|
||||
log.Printf("can't create domain for %s: %v", *name, err)
|
||||
log.Println("you should run this command:")
|
||||
log.Println()
|
||||
log.Printf("zfs destroy %s", zvol)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Printf("created %s", domain.Name)
|
||||
}
|
||||
|
||||
func randomMac() (string, error) {
|
||||
buf := make([]byte, 6)
|
||||
_, err := crand.Read(buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf[0] = (buf[0] | 2) & 0xfe
|
||||
|
||||
return net.HardwareAddr(buf).String(), nil
|
||||
}
|
||||
|
||||
func getName() (string, error) {
|
||||
var names []string
|
||||
nameData, err := data.ReadFile("data/names.json")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(nameData, &names)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return names[rand.Intn(len(names))], nil
|
||||
}
|
||||
|
||||
func run(args ...string) error {
|
||||
log.Println("running command:", strings.Join(args, " "))
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func connectToLibvirt() (*libvirt.Libvirt, error) {
|
||||
c, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't dial libvirt: %w", err)
|
||||
}
|
||||
|
||||
l := libvirt.New(c)
|
||||
|
||||
_, err = l.AuthPolkit()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't auth with polkit: %w", err)
|
||||
}
|
||||
|
||||
if err := l.Connect(); err != nil {
|
||||
return nil, fmt.Errorf("can't connect: %w", err)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func mkVM(l *libvirt.Libvirt, buf *bytes.Buffer) (*libvirt.Domain, error) {
|
||||
domain, err := l.DomainCreateXML(buf.String(), libvirt.DomainNone)
|
||||
return &domain, err
|
||||
}
|
||||
|
||||
type Distro struct {
|
||||
Name string `dhall:"name" json:"name"`
|
||||
DownloadURL string `dhall:"downloadURL" json:"download_url"`
|
||||
Sha256Sum string `dhall:"sha256Sum" json:"sha256_sum"`
|
||||
MinSize int `dhall:"minSize" json:"min_size"`
|
||||
}
|
||||
|
||||
func getDistros() ([]Distro, error) {
|
||||
distroData, err := data.ReadFile("data/distros.dhall")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []Distro
|
||||
err = dhall.Unmarshal(distroData, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
name, err := getName()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(name)
|
||||
}
|
||||
|
||||
func TestGetDistros(t *testing.T) {
|
||||
_, err := getDistros()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomMac(t *testing.T) {
|
||||
mac, err := randomMac()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(mac)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
dhall go goimports gopls cdrkit
|
||||
];
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<domain type="kvm">
|
||||
<name>{{.Name}}</name>
|
||||
<uuid>{{.UUID}}</uuid>
|
||||
<metadata>
|
||||
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
|
||||
<libosinfo:os id="http://nixos.org/nixos/unstable"/>
|
||||
</libosinfo:libosinfo>
|
||||
</metadata>
|
||||
<memory>{{.Memory}}</memory>
|
||||
<currentMemory>{{.Memory}}</currentMemory>
|
||||
<vcpu>2</vcpu>
|
||||
<os>
|
||||
<type arch="x86_64" machine="q35">hvm</type>
|
||||
<boot dev="hd"/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<vmport state="off"/>
|
||||
</features>
|
||||
<cpu mode="host-model"/>
|
||||
<clock offset="utc">
|
||||
<timer name="rtc" tickpolicy="catchup"/>
|
||||
<timer name="pit" tickpolicy="delay"/>
|
||||
<timer name="hpet" present="no"/>
|
||||
</clock>
|
||||
<pm>
|
||||
<suspend-to-mem enabled="no"/>
|
||||
<suspend-to-disk enabled="no"/>
|
||||
</pm>
|
||||
<devices>
|
||||
<emulator>/run/libvirt/nix-emulators/qemu-system-x86_64</emulator>
|
||||
<disk type="block" device="disk">
|
||||
<driver name="qemu" type="raw" cache="none" io="native"/>
|
||||
<source dev="/dev/zvol/{{.ZVol}}"/>
|
||||
<target dev="vda" bus="virtio"/>
|
||||
</disk>
|
||||
<disk type="file" device="cdrom">
|
||||
<driver name="qemu" type="raw"/>
|
||||
<source file="{{.Seed}}"/>
|
||||
<target dev="sda" bus="sata"/>
|
||||
<readonly/>
|
||||
</disk>
|
||||
<controller type="usb" model="qemu-xhci" ports="15"/>
|
||||
<interface type="network">
|
||||
<source network="default"/>
|
||||
<mac address="{{.MACAddress}}"/>
|
||||
<model type="virtio"/>
|
||||
</interface>
|
||||
<console type="pty"/>
|
||||
<channel type="unix">
|
||||
<source mode="bind"/>
|
||||
<target type="virtio" name="org.qemu.guest_agent.0"/>
|
||||
</channel>
|
||||
<channel type="spicevmc">
|
||||
<target type="virtio" name="com.redhat.spice.0"/>
|
||||
</channel>
|
||||
<input type="tablet" bus="usb"/>
|
||||
<graphics type="spice" port="-1" tlsPort="-1" autoport="yes"/>
|
||||
<sound model="ich9"/>
|
||||
<video>
|
||||
<model type="qxl"/>
|
||||
</video>
|
||||
<redirdev bus="usb" type="spicevmc"/>
|
||||
<redirdev bus="usb" type="spicevmc"/>
|
||||
<memballoon model="virtio"/>
|
||||
<rng model="virtio">
|
||||
<backend model="random">/dev/urandom</backend>
|
||||
</rng>
|
||||
</devices>
|
||||
</domain>
|
|
@ -0,0 +1,28 @@
|
|||
-- meta-data --
|
||||
local-hostname: {{.Name}}
|
||||
-- user-data --
|
||||
#cloud-config
|
||||
#vim:syntax=yaml
|
||||
|
||||
cloud_config_modules:
|
||||
- runcmd
|
||||
|
||||
cloud_final_modules:
|
||||
- [users-groups, always]
|
||||
- [scripts-user, once-per-instance]
|
||||
|
||||
users:
|
||||
- name: xe
|
||||
plain_text_passwd: hunter2
|
||||
groups: [ wheel ]
|
||||
sudo: [ "ALL=(ALL) NOPASSWD:ALL" ]
|
||||
shell: /bin/bash
|
||||
ssh-authorized-keys:
|
||||
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPg9gYKVglnO2HQodSJt4z4mNrUSUiyJQ7b+J798bwD9 cadey@shachi
|
||||
|
||||
write_files:
|
||||
- path: /etc/cloud/cloud.cfg.d/80_disable_network_after_firstboot.cfg
|
||||
content: |
|
||||
# Disable network configuration after first boot
|
||||
network:
|
||||
config: disabled
|
Loading…
Reference in New Issue