diff --git a/constants/constants.go b/constants/constants.go new file mode 100644 index 0000000..d2f209f --- /dev/null +++ b/constants/constants.go @@ -0,0 +1,8 @@ +// Package constants This file contains all the constants that can be reused across the project +package constants + +const ( + MB = int64(1024 * 1024) + GB = 1024 * MB + FilePerm = 0644 +) diff --git a/go.mod b/go.mod index bcf26bf..5516a4d 100644 --- a/go.mod +++ b/go.mod @@ -34,9 +34,9 @@ require ( ) require ( - atomicgo.dev/cursor v0.1.3 // indirect + atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect - atomicgo.dev/schedule v0.0.2 // indirect + atomicgo.dev/schedule v0.1.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.9 // indirect github.com/StackExchange/wmi v1.2.1 // indirect @@ -71,7 +71,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gookit/color v1.5.3 // indirect + github.com/gookit/color v1.5.4 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.6 // indirect github.com/jaypipes/pcidb v1.0.1 // indirect @@ -79,7 +79,7 @@ require ( github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect @@ -114,10 +114,10 @@ require ( go.opentelemetry.io/otel/trace v1.31.0 // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.26.0 // indirect + golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.26.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect diff --git a/go.sum b/go.sum index 5a9a026..ddf6706 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,12 @@ atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= atomicgo.dev/cursor v0.1.3 h1:w8GcylMdZRyFzvDiGm3wy3fhZYYT7BwaqNjUFHxo0NU= atomicgo.dev/cursor v0.1.3/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.0.2 h1:2e/4KY6t3wokja01Cyty6qgkQM8MotJzjtqCH70oX2Q= atomicgo.dev/schedule v0.0.2/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -96,6 +98,7 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= +github.com/containerd/containerd/v2 v2.0.0/go.mod h1:j25kDy9P48/ngb1sxWIFfK6GsnqOHoSqo1EpAod20VQ= github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -244,6 +247,7 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -262,6 +266,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g= github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM= +github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY= github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4= @@ -300,6 +305,7 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -318,6 +324,7 @@ github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5 h1:FaZD86+A9mV github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5/go.mod h1:WmKcT8ONmhDQIqQ+HxU+tkGWjzBEyY/KFO8LTGCu4AI= github.com/mudler/yip v1.12.0 h1:wuf103kHd0Zrxb7afquH8DxKeXqVSeqiRhBW/1gDHZQ= github.com/mudler/yip v1.12.0/go.mod h1:gwH7iGcr1Jimox2xKtN2AprEO00GzY7smvuycqCL7+Y= +github.com/mudler/yip v1.13.0/go.mod h1:gwH7iGcr1Jimox2xKtN2AprEO00GzY7smvuycqCL7+Y= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -330,11 +337,13 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -359,6 +368,7 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.63 h1:fHlrpFiI9qLtEU0TWDWMU+tAt4qKJ/s157BEAPtGm8w= github.com/pterm/pterm v0.12.63/go.mod h1:Bq1eoUJ6BhUzzXG8WxA4l7T3s7d3Ogwg7v9VXlsVat0= +github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo= github.com/qeesung/image2ascii v1.0.1 h1:Fe5zTnX/v/qNC3OC4P/cfASOXS501Xyw2UUcgrLgtp4= github.com/qeesung/image2ascii v1.0.1/go.mod h1:kZKhyX0h2g/YXa/zdJR3JnLnJ8avHjZ3LrvEKSYyAyU= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -372,8 +382,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/saferwall/pe v1.5.4 h1:tLmMggEMUfeqrpJ25zS/okUQmyFdD5xWKL2+z9njCqg= github.com/saferwall/pe v1.5.4/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= +github.com/saferwall/pe v1.5.6/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc= github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -431,6 +443,7 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zcalusic/sysinfo v1.1.2 h1:38KUgZQmCxlN9vUTt4miis4rU5ISJXGXOJ2rY7bMC8g= github.com/zcalusic/sysinfo v1.1.2/go.mod h1:NX+qYnWGtJVPV0yWldff9uppNKU4h40hJIRPf/pGLv4= +github.com/zcalusic/sysinfo v1.1.3/go.mod h1:NX+qYnWGtJVPV0yWldff9uppNKU4h40hJIRPf/pGLv4= go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -506,6 +519,7 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -568,6 +582,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -626,6 +641,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -633,6 +649,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -644,6 +661,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/types/fs.go b/types/fs.go index 01ad8aa..50153f4 100644 --- a/types/fs.go +++ b/types/fs.go @@ -3,13 +3,32 @@ package types import ( "io/fs" "os" + "time" ) // KairosFS is our interface for methods that need an FS type KairosFS interface { - ReadFile(filename string) ([]byte, error) - Stat(name string) (fs.FileInfo, error) Open(name string) (fs.File, error) + Chmod(name string, mode os.FileMode) error + Create(name string) (*os.File, error) + Mkdir(name string, perm os.FileMode) error + Stat(name string) (os.FileInfo, error) + Lstat(name string) (os.FileInfo, error) + RemoveAll(path string) error + ReadFile(filename string) ([]byte, error) + Readlink(name string) (string, error) RawPath(name string) (string, error) + ReadDir(dirname string) ([]fs.DirEntry, error) + Remove(name string) error + OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) WriteFile(filename string, data []byte, perm os.FileMode) error + Rename(oldpath, newpath string) error + Truncate(name string, size int64) error + Chown(name string, uid, git int) error + Chtimes(name string, atime, mtime time.Time) error + Glob(pattern string) ([]string, error) + Lchown(name string, uid, git int) error + Link(oldname, newname string) error + PathSeparator() rune + Symlink(oldname, newname string) error } diff --git a/utils/rawDisk.go b/utils/rawDisk.go new file mode 100644 index 0000000..6c455e6 --- /dev/null +++ b/utils/rawDisk.go @@ -0,0 +1,188 @@ +package utils + +import ( + "archive/tar" + "compress/gzip" + "encoding/binary" + "fmt" + "io" + "io/fs" + "os" + + "github.com/kairos-io/kairos-sdk/constants" + "github.com/kairos-io/kairos-sdk/types" +) + +// Raw2Azure converts a raw disk to a VHD disk compatible with Azure +// All VHDs on Azure must have a virtual size aligned to 1 MB (1024 × 1024 bytes) +// The Hyper-V virtual hard disk (VHDX) format isn't supported in Azure, only fixed VHD +func Raw2Azure(source string, logger types.KairosLogger) error { + logger.Logger.Info().Str("source", source).Msg("Converting raw disk to Azure VHD") + // Copy raw to new image with VHD appended + // rename file to .vhd + err := os.Rename(source, fmt.Sprintf("%s.vhd", source)) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error renaming raw image to vhd") + return err + } + // Open it + vhdFile, _ := os.OpenFile(fmt.Sprintf("%s.vhd", source), os.O_APPEND|os.O_WRONLY, constants.FilePerm) + // Calculate rounded size + info, err := vhdFile.Stat() + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error getting file info") + return err + } + actualSize := info.Size() + finalSizeBytes := ((actualSize + 1024*1024 - 1) / 1024 * 1024) * 1024 * 1024 + // Don't forget to remove 512 bytes for the header that we are going to add afterwards! + finalSizeBytes = finalSizeBytes - 512 + // For smaller than 1 MB images, this calculation doesn't work, so we round up to 1 MB + if finalSizeBytes == 0 { + finalSizeBytes = 1*1024*1024 - 512 + } + if actualSize != finalSizeBytes { + logger.Logger.Info().Int64("actualSize", actualSize).Int64("finalSize", finalSizeBytes).Msg("Resizing image") + // If you do not seek, you will override the data + _, err = vhdFile.Seek(0, io.SeekEnd) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error seeking to end") + return err + } + err = vhdFile.Truncate(finalSizeBytes) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error truncating file") + return err + } + } + // Transform it to VHD + info, err = vhdFile.Stat() // Stat again to get the new size + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error getting file info") + return err + } + size := uint64(info.Size()) + header := newVHDFixed(size) + err = binary.Write(vhdFile, binary.BigEndian, header) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error writing header") + return err + } + err = vhdFile.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error closing file") + return err + } + return nil +} + +// Raw2Gce transforms an image from RAW format into GCE format +// The RAW image file must have a size in an increment of 1 GB. For example, the file must be either 10 GB or 11 GB but not 10.5 GB. +// The disk image filename must be disk.raw. +// The compressed file must be a .tar.gz file that uses gzip compression and the --format=oldgnu option for the tar utility. +func Raw2Gce(source string, kairosFs types.KairosFS, logger types.KairosLogger) error { + logger.Logger.Info().Msg("Transforming raw image into gce format") + actImg, err := kairosFs.OpenFile(source, os.O_CREATE|os.O_APPEND|os.O_WRONLY, constants.FilePerm) + if err != nil { + logger.Logger.Error().Err(err).Str("file", source).Msg("Error opening file") + return err + } + info, err := actImg.Stat() + if err != nil { + logger.Logger.Error().Err(err).Str("file", source).Msg("Error getting file info") + return err + } + actualSize := info.Size() + finalSizeGB := actualSize/constants.GB + 1 + finalSizeBytes := finalSizeGB * constants.GB + logger.Logger.Info().Int64("current", actualSize).Int64("final", finalSizeGB).Str("file", source).Msg("Resizing image") + // REMEMBER TO SEEK! + _, err = actImg.Seek(0, io.SeekEnd) + if err != nil { + logger.Logger.Error().Err(err).Str("file", source).Msg("Error seeking to end") + return err + } + err = actImg.Truncate(finalSizeBytes) + if err != nil { + logger.Logger.Error().Err(err).Str("file", source).Msg("Error truncating file") + return err + } + err = actImg.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("file", source).Msg("Error closing file") + return err + } + + // Tar gz the image + + // Create destination file + file, err := kairosFs.Create(fmt.Sprintf("%s.tar.gz", source)) + if err != nil { + logger.Logger.Error().Err(err).Str("destination", fmt.Sprintf("%s.tar.gz", source)).Msg("Error creating destination file") + return err + } + logger.Logger.Info().Str("destination", file.Name()).Msg("Compressing raw image into a tar.gz") + + defer func(file *os.File) { + err = file.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("destination", file.Name()).Msg("Error closing destination file") + } + }(file) + // Create gzip writer + gzipWriter, err := gzip.NewWriterLevel(file, gzip.BestSpeed) + if err != nil { + return err + } + defer func(gzipWriter *gzip.Writer) { + err := gzipWriter.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("destination", file.Name()).Msg("Error closing gzip writer") + } + }(gzipWriter) + // Create tarwriter pointing to our gzip writer + tarWriter := tar.NewWriter(gzipWriter) + defer func(tarWriter *tar.Writer) { + err = tarWriter.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("destination", file.Name()).Msg("Error closing tar writer") + } + }(tarWriter) + + // Open disk.raw + sourceFile, _ := kairosFs.Open(source) + sourceStat, _ := sourceFile.Stat() + defer func(sourceFile fs.File) { + err = sourceFile.Close() + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error closing source file") + } + }(sourceFile) + + // Add disk.raw file + header := &tar.Header{ + Name: sourceStat.Name(), + Size: sourceStat.Size(), + Mode: int64(sourceStat.Mode()), + Format: tar.FormatGNU, + } + // Write header with all the info + err = tarWriter.WriteHeader(header) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error writing header") + return err + } + // copy the actual data + _, err = io.Copy(tarWriter, sourceFile) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error copying data") + return err + } + // Remove full raw image, we already got the compressed one + err = kairosFs.RemoveAll(source) + if err != nil { + logger.Logger.Error().Err(err).Str("source", source).Msg("Error removing full raw image") + return err + } + return nil +} diff --git a/utils/vhd.go b/utils/vhd.go new file mode 100644 index 0000000..91068e8 --- /dev/null +++ b/utils/vhd.go @@ -0,0 +1,146 @@ +/* +Copyright © 2022 - 2024 SUSE LLC +Copyright © 2024 Kairos authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "math" + "time" + + uuidPkg "github.com/google/uuid" +) + +// This file contains utils to work with VHD disks + +type VHDHeader struct { + Cookie [8]byte // Cookies are used to uniquely identify the original creator of the hard disk image + Features [4]byte // This is a bit field used to indicate specific feature support. Can be 0x00000000 (no features), 0x00000001 (Temporary, candidate for deletion on shutdown) or 0x00000002 (Reserved) + FileFormatVersion [4]byte // Divided into a major/minor version and matches the version of the specification used in creating the file. + DataOffset [8]byte // For fixed disks, this field should be set to 0xFFFFFFFF. + Timestamp [4]byte // Stores the creation time of a hard disk image. This is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. + CreatorApplication [4]byte // Used to document which application created the hard disk. + CreatorVersion [4]byte // This field holds the major/minor version of the application that created the hard disk image. + CreatorHostOS [4]byte // This field stores the type of host operating system this disk image is created on. + OriginalSize [8]byte // This field stores the size of the hard disk in bytes, from the perspective of the virtual machine, at creation time. Info only + CurrentSize [8]byte // This field stores the current size of the hard disk, in bytes, from the perspective of the virtual machine. + DiskGeometry [4]byte // This field stores the cylinder, heads, and sectors per track value for the hard disk. + DiskType [4]byte // Fixed = 2, Dynamic = 3, Differencing = 4 + Checksum [4]byte // This field holds a basic checksum of the hard disk footer. It is just a one’s complement of the sum of all the bytes in the footer without the checksum field. + UniqueID [16]byte // This is a 128-bit universally unique identifier (UUID). + SavedState [1]byte // This field holds a one-byte flag that describes whether the system is in saved state. If the hard disk is in the saved state the value is set to 1 + Reserved [427]byte // This field contains zeroes. +} + +// Lots of magic numbers here, but they are all defined in the VHD format spec +func newVHDFixed(size uint64) VHDHeader { + header := VHDHeader{} + hexToField("00000002", header.Features[:]) + hexToField("00010000", header.FileFormatVersion[:]) + hexToField("ffffffffffffffff", header.DataOffset[:]) + t := uint32(time.Now().Unix() - 946684800) + binary.BigEndian.PutUint32(header.Timestamp[:], t) + hexToField("656c656d", header.CreatorApplication[:]) // Cos + hexToField("73757365", header.CreatorHostOS[:]) // SUSE + binary.BigEndian.PutUint64(header.OriginalSize[:], size) + binary.BigEndian.PutUint64(header.CurrentSize[:], size) + // Divide size into 512 to get the total sectors + totalSectors := float64(size / 512) + geometry := chsCalculation(uint64(totalSectors)) + binary.BigEndian.PutUint16(header.DiskGeometry[:2], uint16(geometry.cylinders)) + header.DiskGeometry[2] = uint8(geometry.heads) + header.DiskGeometry[3] = uint8(geometry.sectorsPerTrack) + hexToField("00000002", header.DiskType[:]) // Fixed 0x00000002 + hexToField("00000000", header.Checksum[:]) + uuid := uuidPkg.New() // Generate a new UUID v4! + copy(header.UniqueID[:], uuid.String()) + generateChecksum(&header) + return header +} + +// generateChecksum generates the checksum of the vhd header +// Lifted from the official VHD Format Spec +func generateChecksum(header *VHDHeader) { + buffer := new(bytes.Buffer) + _ = binary.Write(buffer, binary.BigEndian, header) + checksum := 0 + bb := buffer.Bytes() + for counter := 0; counter < 512; counter++ { + checksum += int(bb[counter]) + } + binary.BigEndian.PutUint32(header.Checksum[:], uint32(^checksum)) +} + +// hexToField decodes an hex to bytes and copies it to the given header field +func hexToField(hexs string, field []byte) { + h, _ := hex.DecodeString(hexs) + copy(field, h) +} + +// chs is a simple struct to represent the cylinders/heads/sectors for a given sector count +type chs struct { + cylinders uint + heads uint + sectorsPerTrack uint +} + +// chsCalculation calculates the cylinders, headers and sectors per track for a given sector count +// Exactly the same code on the official VHD format spec +func chsCalculation(sectors uint64) chs { + var sectorsPerTrack, + heads, + cylinderTimesHeads, + cylinders float64 + totalSectors := float64(sectors) + + if totalSectors > 65535*16*255 { + totalSectors = 65535 * 16 * 255 + } + + if totalSectors >= 65535*16*63 { + sectorsPerTrack = 255 + heads = 16 + cylinderTimesHeads = math.Floor(totalSectors / sectorsPerTrack) + } else { + sectorsPerTrack = 17 + cylinderTimesHeads = math.Floor(totalSectors / sectorsPerTrack) + heads = math.Floor((cylinderTimesHeads + 1023) / 1024) + if heads < 4 { + heads = 4 + } + if (cylinderTimesHeads >= (heads * 1024)) || heads > 16 { + sectorsPerTrack = 31 + heads = 16 + cylinderTimesHeads = math.Floor(totalSectors / sectorsPerTrack) + } + if cylinderTimesHeads >= (heads * 1024) { + sectorsPerTrack = 63 + heads = 16 + cylinderTimesHeads = math.Floor(totalSectors / sectorsPerTrack) + } + } + + cylinders = cylinderTimesHeads / heads + + return chs{ + cylinders: uint(cylinders), + heads: uint(heads), + sectorsPerTrack: uint(sectorsPerTrack), + } +}