mirror of
https://github.com/kairos-io/kairos-sdk.git
synced 2025-05-06 15:17:12 +00:00
* Introduce versioneer package to replace the naming.sh script in kairos-io/kairos Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement BootableName for bootable artifacts also introduce Version and SoftwareVersion fields Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Refactor to introduce commondName to be used with ContainerName Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement ContainerName and add missing tests Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Introduce NewArtifactFromJSON Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Introduce NewFromOSRelease to be used within a Kairos image See also discussion: https://github.com/kairos-io/kairos/discussions/2035 Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Introduce 3 methods to find releases (TODO: implement them) Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Use existing OSRelease method and remove the new one the existing method had to be adapted to accept and optional path to the os-release file to allow the tests to pass a tmp file. Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Introduce TagList and some basic filtering of tags also introduce RegistryInspector which finds tags from a container registry for a specific repository. Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement OtherVersions method of TagList to return different Kairos versions of the exact same artifact. Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * [WIP] Add TODOs for 2 more methods Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement Sorted() method on TagList Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement RSorted and change OtherVersions test to ensure it also returns older versions too. Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement NewerVersions method Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Make json file pretty Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement OtherSoftwareVersions and NewerSoftwareVersions Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement NewerAnyVersions and OtherAnyVersions Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Make TagList a struct so that it has an "Artifact" field Now the Artifact has a TagList method that returns a TagList which is associated to the specific Artifact. All methods of TagList are now available to the Artifact. E.g. tagList, _ := artifact.TagList("quay.io/kairos") tagList.NewerAnyVersion(...) Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Use the field Artifact in TagList methods instead of needing an argument Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Fix linting errors Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Restructure files and introduce main.go for versioneer to provide a user interface that will replace naming.sh script of the kairos repository Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Fix imports Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement bootable-artifact-name cli command Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement BaseContainerName method Signed-off-by: Dimitris Karakasilis <dimitris@spectrocloud.com> * Create a wrapper command base-container-artifact-name Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com> * Extract the cli command to the packag to be re-used in kairos-agent Signed-off-by: Dimitris Karakasilis <dimitris@spectrocloud.com> * Go back to original package structure and just nest the cli Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Move the versioneer "main" package outside the tree to allow it to be imported in kairos-agent Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Allow setting the cli flags using environment variables Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Implement os-release-variables command to replace the logic in Earthly and Dockerfiles Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Fix test Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Fix TODO in Readme Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Dry the creation of the Artifact Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> --------- Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> Signed-off-by: Dimitris Karakasilis <dimitris@spectrocloud.com> Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com> Co-authored-by: Mauro Morales <mauro.morales@spectrocloud.com> Co-authored-by: Dimitris Karakasilis <dimitris@spectrocloud.com>
296 lines
5.8 KiB
Go
296 lines
5.8 KiB
Go
package utils
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"image"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/denisbrodbeck/machineid"
|
|
"github.com/joho/godotenv"
|
|
"github.com/pterm/pterm"
|
|
"github.com/qeesung/image2ascii/convert"
|
|
)
|
|
|
|
const (
|
|
systemd = "systemd"
|
|
openrc = "openrc"
|
|
unknown = "unknown"
|
|
)
|
|
|
|
func SH(c string) (string, error) {
|
|
cmd := exec.Command("/bin/sh", "-c", c)
|
|
cmd.Env = os.Environ()
|
|
o, err := cmd.CombinedOutput()
|
|
return string(o), err
|
|
}
|
|
|
|
func SHInDir(c, dir string, envs ...string) (string, error) {
|
|
cmd := exec.Command("/bin/sh", "-c", c)
|
|
cmd.Env = append(os.Environ(), envs...)
|
|
cmd.Dir = dir
|
|
o, err := cmd.CombinedOutput()
|
|
return string(o), err
|
|
}
|
|
|
|
func Exists(path string) bool {
|
|
_, err := os.Stat(path)
|
|
|
|
return !os.IsNotExist(err)
|
|
}
|
|
|
|
// UUID TODO: move this into a machine submodule
|
|
func UUID() string {
|
|
if os.Getenv("UUID") != "" {
|
|
return os.Getenv("UUID")
|
|
}
|
|
id, _ := machineid.ID()
|
|
hostname, _ := os.Hostname()
|
|
return fmt.Sprintf("%s-%s", id, hostname)
|
|
}
|
|
|
|
// OSRelease finds the value of the specified key in the /etc/os-release file
|
|
// or, if a second argument is passed, on the file specified by the second argument.
|
|
// (optionally file argument is there for testing reasons).
|
|
func OSRelease(key string, file ...string) (string, error) {
|
|
var osReleaseFile string
|
|
|
|
if len(file) > 1 {
|
|
return "", errors.New("too many arguments passed")
|
|
}
|
|
if len(file) > 0 {
|
|
osReleaseFile = file[0]
|
|
} else {
|
|
osReleaseFile = "/etc/os-release"
|
|
}
|
|
release, err := godotenv.Read(osReleaseFile)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
kairosKey := "KAIROS_" + key
|
|
v, exists := release[kairosKey]
|
|
if !exists {
|
|
// We try with the old naming without the prefix in case the key wasn't found
|
|
v, exists = release[key]
|
|
if !exists {
|
|
return "", fmt.Errorf("%s key not found", kairosKey)
|
|
}
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func FindCommand(def string, options []string) string {
|
|
for _, p := range options {
|
|
path, err := exec.LookPath(p)
|
|
if err == nil {
|
|
return path
|
|
}
|
|
}
|
|
|
|
// Otherwise return default
|
|
return def
|
|
}
|
|
|
|
func K3sBin() string {
|
|
for _, p := range []string{"/usr/bin/k3s", "/usr/local/bin/k3s"} {
|
|
if _, err := os.Stat(p); err == nil {
|
|
return p
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func WriteEnv(envFile string, config map[string]string) error {
|
|
content, _ := os.ReadFile(envFile)
|
|
env, _ := godotenv.Unmarshal(string(content))
|
|
|
|
for key, val := range config {
|
|
env[key] = val
|
|
}
|
|
|
|
return godotenv.Write(env, envFile)
|
|
}
|
|
|
|
func Flavor() string {
|
|
v, err := OSRelease("FLAVOR")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// GetInit Return the init system used by the OS
|
|
func GetInit() string {
|
|
for _, file := range []string{"/run/systemd/system", "/sbin/systemctl", "/usr/bin/systemctl", "/usr/sbin/systemctl", "/usr/bin/systemctl"} {
|
|
_, err := os.Stat(file)
|
|
// Found systemd
|
|
if err == nil {
|
|
return systemd
|
|
}
|
|
}
|
|
|
|
for _, file := range []string{"/sbin/openrc", "/usr/sbin/openrc", "/bin/openrc", "/usr/bin/openrc"} {
|
|
_, err := os.Stat(file)
|
|
// Found openrc
|
|
if err == nil {
|
|
return openrc
|
|
}
|
|
}
|
|
|
|
return unknown
|
|
}
|
|
|
|
func Name() string {
|
|
v, err := OSRelease("NAME")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return strings.ReplaceAll(v, "kairos-", "")
|
|
}
|
|
|
|
func IsOpenRCBased() bool {
|
|
return GetInit() == openrc
|
|
}
|
|
|
|
func ShellSTDIN(s, c string) (string, error) {
|
|
cmd := exec.Command("/bin/sh", "-c", c)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = bytes.NewBuffer([]byte(s))
|
|
o, err := cmd.CombinedOutput()
|
|
return string(o), err
|
|
}
|
|
|
|
func SetEnv(env []string) {
|
|
for _, e := range env {
|
|
pair := strings.SplitN(e, "=", 2)
|
|
if len(pair) >= 2 {
|
|
os.Setenv(pair[0], pair[1])
|
|
}
|
|
}
|
|
}
|
|
|
|
func OnSignal(fn func(), sig ...os.Signal) {
|
|
sigs := make(chan os.Signal, 1)
|
|
signal.Notify(sigs, sig...)
|
|
go func() {
|
|
<-sigs
|
|
fn()
|
|
}()
|
|
}
|
|
|
|
func Shell() *exec.Cmd {
|
|
cmd := exec.Command("/bin/sh")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = os.Stdin
|
|
return cmd
|
|
}
|
|
|
|
func Prompt(t string) (string, error) {
|
|
if t != "" {
|
|
pterm.Info.Println(t)
|
|
}
|
|
answer, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return strings.TrimSpace(answer), nil
|
|
}
|
|
|
|
func PrintBanner(d []byte) {
|
|
img, _, _ := image.Decode(bytes.NewReader(d))
|
|
|
|
convertOptions := convert.DefaultOptions
|
|
convertOptions.FixedWidth = 100
|
|
convertOptions.FixedHeight = 40
|
|
|
|
// Create the image converter
|
|
converter := convert.NewImageConverter()
|
|
fmt.Print(converter.Image2ASCIIString(img, &convertOptions))
|
|
}
|
|
|
|
func Reboot() {
|
|
pterm.Info.Println("Rebooting node")
|
|
SH("reboot") //nolint:errcheck
|
|
}
|
|
|
|
func PowerOFF() {
|
|
pterm.Info.Println("Shutdown node")
|
|
if IsOpenRCBased() {
|
|
SH("poweroff") //nolint:errcheck
|
|
} else {
|
|
SH("shutdown") //nolint:errcheck
|
|
}
|
|
}
|
|
|
|
func Version() string {
|
|
v, err := OSRelease("VERSION")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
v = strings.ReplaceAll(v, "+k3s1-Kairos", "-")
|
|
v = strings.ReplaceAll(v, "+k3s-Kairos", "-")
|
|
return strings.ReplaceAll(v, "Kairos", "")
|
|
}
|
|
|
|
func ListToOutput(rels []string, output string) []string {
|
|
switch strings.ToLower(output) {
|
|
case "yaml":
|
|
d, _ := yaml.Marshal(rels)
|
|
return []string{string(d)}
|
|
case "json":
|
|
d, _ := json.Marshal(rels)
|
|
return []string{string(d)}
|
|
default:
|
|
return rels
|
|
}
|
|
}
|
|
|
|
func GetInterfaceIP(in string) string {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
fmt.Println("failed getting system interfaces")
|
|
return ""
|
|
}
|
|
for _, i := range ifaces {
|
|
if i.Name == in {
|
|
addrs, _ := i.Addrs()
|
|
// handle err
|
|
for _, addr := range addrs {
|
|
var ip net.IP
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
ip = v.IP
|
|
case *net.IPAddr:
|
|
ip = v.IP
|
|
}
|
|
if ip != nil {
|
|
return ip.String()
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetCurrentPlatform returns the current platform in docker style `linux/amd64` for use with image utils
|
|
func GetCurrentPlatform() string {
|
|
return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
|
|
}
|