diff --git a/README.md b/README.md index dcad4e5..0c28a58 100644 --- a/README.md +++ b/README.md @@ -93,13 +93,12 @@ Execute the \ file. ### II. Path Expressions -The syntax is pretty fast-forward, it uses 3 levels to find your configuration line : `subject@alt.field`. +The syntax is pretty fast-forward, it uses 3 levels to find your configuration line : `[subject.path].[field.path]`. | Field | Description | Example | | --------- | :----------------------------------- | -------------------------- | -| `subject` | The name of the program to configure. Available program names are listed in this [table](tablefile). | `sshd`, `httpd`, `nginx` | -| `alt` | Alternative file if there is more than one for a package. If **omitted** it will default to the _main_ configuration file. | `httpd@main`, `http@ports` | -| `field` | Field is a dot-separated chain of strings that match with a configuration field. If **omitted**, the \ will just be added at the end of the configuration file. In the same way if the field does not point to a raw field but a parent or group containing fields, the \ will be added at the end. | `sshd.AllowGroups`, `nginx.http.gzip` | +| `subject.path` | Dot-separated path to the configuration file to edit. Available program paths are listed in this [table](tablefile). | `sshd`, `httpd`, `nginx`, `nginx.vhost.default` | +| `field.path` | Dot-separated chain of strings that match with a configuration field. If **omitted**, the \ will just be added at the end of the configuration file. In the same way if the field does not point to a raw field but a parent or group containing fields, the \ will be added at the end of the group. | `sshd.AllowGroups`, `nginx.http.gzip` | @@ -117,6 +116,7 @@ upd ins nginx ssh sslh cnf nginx.http.gzip on +cnf nginx.vhost.new-site ./localConfFile ser enable nginx ser start nginx diff --git a/internal/pkg/apk.go b/internal/pkg/apk.go new file mode 100644 index 0000000..53803b2 --- /dev/null +++ b/internal/pkg/apk.go @@ -0,0 +1,29 @@ +package pkg + +import ( + "os/exec" +) + +type Apk struct{} + +func (d Apk) Name() string { return "apk" } + +func (d Apk) Fetch() bool { + _, err := exec.Command(d.Name(), "update").Output() + return err == nil +} + +func (d Apk) Upgrade() bool { + _, err := exec.Command(d.Name(), "upgrade").Output() + return err == nil +} + +func (d Apk) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "add", _pkg).Output() + return err == nil +} + +func (d Apk) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "del", _pkg).Output() + return err == nil +} diff --git a/internal/pkg/apt.go b/internal/pkg/apt.go new file mode 100644 index 0000000..62b71dd --- /dev/null +++ b/internal/pkg/apt.go @@ -0,0 +1,40 @@ +package pkg + +import ( + "os/exec" +) + +type Apt struct{} + +func (d Apt) Name() string { return "apt-get" } + +func (d Apt) Fetch() bool { + _, err := exec.Command(d.Name(), "update").Output() + return err == nil +} + +func (d Apt) Upgrade() bool { + _, err := exec.Command(d.Name(), "upgrade").Output() + if err != nil { + return false + } + + _, err = exec.Command(d.Name(), "dist-upgrade").Output() + if err != nil { + return false + } + + exec.Command(d.Name(), "autoremove").Run() + return true +} + +func (d Apt) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "install", _pkg).Output() + return err == nil +} + +func (d Apt) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "remove", _pkg).Output() + exec.Command(d.Name(), "autoremove").Run() + return err == nil +} diff --git a/internal/pkg/common.go b/internal/pkg/common.go index b03d2de..ef40d7b 100644 --- a/internal/pkg/common.go +++ b/internal/pkg/common.go @@ -2,6 +2,8 @@ package pkg // PackageManager is the common interface for all package-manager drivers (e.g. `dpkg` for debian-based, `pacman` for arch) type PackageManager interface { + // Name of executable (to check if installed and for debug) + Name() string // Fetch updates the package cache/databse Fetch() bool // Upgrade already installed packages and whatever can be upgraded with warranty (e.g. kernel) diff --git a/internal/pkg/dnf.go b/internal/pkg/dnf.go new file mode 100644 index 0000000..a8da329 --- /dev/null +++ b/internal/pkg/dnf.go @@ -0,0 +1,30 @@ +package pkg + +import ( + "os/exec" +) + +type Dnf struct{} + +func (d Dnf) Name() string { return "dnf" } + +func (d Dnf) Fetch() bool { + return true +} + +func (d Dnf) Upgrade() bool { + _, err := exec.Command(d.Name(), "upgrade").Output() + exec.Command(d.Name(), "autoremove").Run() + return err == nil +} + +func (d Dnf) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "install", _pkg).Output() + return err == nil +} + +func (d Dnf) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "remove", _pkg).Output() + exec.Command(d.Name(), "autoremove").Run() + return err == nil +} diff --git a/internal/pkg/eopkg.go b/internal/pkg/eopkg.go new file mode 100644 index 0000000..736e77b --- /dev/null +++ b/internal/pkg/eopkg.go @@ -0,0 +1,30 @@ +package pkg + +import ( + "os/exec" +) + +type Eopkg struct{} + +func (d Eopkg) Name() string { return "eopkg" } + +func (d Eopkg) Fetch() bool { + return true +} + +func (d Eopkg) Upgrade() bool { + _, err := exec.Command(d.Name(), "upgrade").Output() + exec.Command(d.Name(), "remove-orphans").Run() + return err == nil +} + +func (d Eopkg) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "install", _pkg).Output() + return err == nil +} + +func (d Eopkg) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "remove", _pkg).Output() + exec.Command(d.Name(), "remove-orphans").Run() + return err == nil +} diff --git a/internal/pkg/loader.go b/internal/pkg/loader.go new file mode 100644 index 0000000..aca5860 --- /dev/null +++ b/internal/pkg/loader.go @@ -0,0 +1,80 @@ +package pkg + +import ( + "encoding/json" + "errors" + "os" + "os/exec" + "path/filepath" + "strings" +) + +var ErrUnknownDistribution = errors.New("unknown linux distribution") +var ErrNoCandidateInstalled = errors.New("no package-manager candidate installed") +var ErrNoDriverFound = errors.New("no driver found for the package manager") + +func Load() (PackageManager, error) { + + // 1. get distro name + out, err := exec.Command("lsb_release", "-is").Output() + if err != nil { + return nil, err + } + + distro := strings.ToLower(strings.Trim(string(out), " \n\t")) + + // 2. load config file + driverTable := filepath.Join(os.Getenv("GOPATH"), "src/git.xdrm.io/xdrm-brackets/nix-amer/meta/pkg-drivers.json") + file, err := os.Open(driverTable) + if err != nil { + return nil, err + } + defer file.Close() + + // 3. Parse json + table := make(map[string][]string) + decoder := json.NewDecoder(file) + if err := decoder.Decode(&table); err != nil { + return nil, err + } + decoder = nil + + // 4. Get available package-manager list for distro + available, exists := table[distro] + if !exists || len(available) < 1 { + return nil, ErrUnknownDistribution + } + + // 5. Check each available package-manager in order + selected := "" + for _, current := range available { + if exec.Command("which", current).Run() == nil { + selected = current + break + } + } + + // no candidate installed + if len(selected) < 1 { + return nil, ErrNoCandidateInstalled + } + + // 6. Instanciate + switch selected { + case "apt-get": + return new(Apt), nil + case "apk": + return new(Apk), nil + case "eopkg": + return new(Eopkg), nil + case "pacman": + return new(Pacman), nil + case "dnf": + return new(Dnf), nil + case "yum": + return new(Yum), nil + } + + return nil, ErrNoDriverFound + +} diff --git a/internal/pkg/pacman.go b/internal/pkg/pacman.go new file mode 100644 index 0000000..d2822af --- /dev/null +++ b/internal/pkg/pacman.go @@ -0,0 +1,30 @@ +package pkg + +import ( + "os/exec" +) + +type Pacman struct{} + +func (d Pacman) Name() string { return "pacman" } + +func (d Pacman) Fetch() bool { + return true +} + +func (d Pacman) Upgrade() bool { + _, err := exec.Command(d.Name(), "-Syu").Output() + exec.Command(d.Name(), "-Ru").Run() + return err == nil +} + +func (d Pacman) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "-S", _pkg).Output() + return err == nil +} + +func (d Pacman) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "-R", _pkg).Output() + exec.Command(d.Name(), "-Ru").Run() + return err == nil +} diff --git a/internal/pkg/yum.go b/internal/pkg/yum.go new file mode 100644 index 0000000..3abf77f --- /dev/null +++ b/internal/pkg/yum.go @@ -0,0 +1,28 @@ +package pkg + +import ( + "os/exec" +) + +type Yum struct{} + +func (d Yum) Name() string { return "yum" } + +func (d Yum) Fetch() bool { + return true +} + +func (d Yum) Upgrade() bool { + _, err := exec.Command(d.Name(), "update").Output() + return err == nil +} + +func (d Yum) Install(_pkg string) bool { + _, err := exec.Command(d.Name(), "install", _pkg).Output() + return err == nil +} + +func (d Yum) Remove(_pkg string) bool { + _, err := exec.Command(d.Name(), "remove", _pkg).Output() + return err == nil +} diff --git a/meta/pkg-drivers.json b/meta/pkg-drivers.json index b873950..956d76e 100644 --- a/meta/pkg-drivers.json +++ b/meta/pkg-drivers.json @@ -1,17 +1,13 @@ { - "ubuntu": "dpkg", - "debian": "dpkg", + "ubuntu": [ "apt-get" ], + "debian": [ "apt-get" ], - "alpine": "apk", + "alpine": [ "apk" ], - "solus": "eopkg", + "solus": [ "eopkg" ], - "arch": "pacman", + "arch": [ "pacman" ], - "fedora": "rpm", - "rhel": "rpm", - "suse": "rpm", - "opensuse": "rpm", - - "gentoo": "emerge" + "centos": [ "dnf", "yum" ], + "fedora": [ "dnf", "yum" ] } \ No newline at end of file