View

Large diffs are not rendered by default.

Oops, something went wrong.
View
302 pkg.go
@@ -0,0 +1,302 @@
+// Mgmt
+// Copyright (C) 2013-2016+ James Shubin and the project contributors
+// Written by James Shubin <james@shubin.ca> and the project contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ //"packagekit" // TODO
+ "errors"
+ "fmt"
+ "log"
+ "strings"
+)
+
+type PkgRes struct {
+ BaseRes `yaml:",inline"`
+ State string `yaml:"state"` // state: installed, uninstalled, newest, <version>
+ AllowUntrusted bool `yaml:"allowuntrusted"` // allow untrusted packages to be installed?
+ AllowNonFree bool `yaml:"allownonfree"` // allow nonfree packages to be found?
+ AllowUnsupported bool `yaml:"allowunsupported"` // allow unsupported packages to be found?
+}
+
+func NewPkgRes(name, state string, allowuntrusted, allownonfree, allowunsupported bool) *PkgRes {
+ return &PkgRes{
+ BaseRes: BaseRes{
+ Name: name,
+ events: make(chan Event),
+ vertex: nil,
+ },
+ State: state,
+ AllowUntrusted: allowuntrusted,
+ AllowNonFree: allownonfree,
+ AllowUnsupported: allowunsupported,
+ }
+}
+
+func (obj *PkgRes) GetRes() string {
+ return "Pkg"
+}
+
+func (obj *PkgRes) Validate() bool {
+
+ if obj.State == "" {
+ return false
+ }
+
+ return true
+}
+
+// use UpdatesChanged signal to watch for changes
+// TODO: https://github.com/hughsie/PackageKit/issues/109
+// TODO: https://github.com/hughsie/PackageKit/issues/110
+func (obj *PkgRes) Watch() {
+ if obj.IsWatching() {
+ return
+ }
+ obj.SetWatching(true)
+ defer obj.SetWatching(false)
+
+ bus := NewBus()
+ if bus == nil {
+ log.Fatal("Can't connect to PackageKit bus.")
+ }
+ defer bus.Close()
+
+ ch, err := bus.WatchChanges()
+ if err != nil {
+ log.Fatalf("Error adding signal match: %v", err)
+ }
+
+ var send = false // send event?
+ var exit = false
+ var dirty = false
+
+ for {
+ if DEBUG {
+ log.Printf("Pkg[%v]: Watching...", obj.GetName())
+ }
+
+ obj.SetState(resStateWatching) // reset
+ select {
+ case event := <-ch:
+
+ // FIXME: ask packagekit for info on what packages changed
+ if DEBUG {
+ log.Printf("Pkg[%v]: Event: %v", obj.GetName(), event.Name)
+ }
+
+ // since the chan is buffered, remove any supplemental
+ // events since they would just be duplicates anyways!
+ for len(ch) > 0 { // we can detect pending count here!
+ <-ch // discard
+ }
+
+ obj.SetConvergedState(resConvergedNil)
+ send = true
+ dirty = true
+
+ case event := <-obj.events:
+ obj.SetConvergedState(resConvergedNil)
+ if exit, send = obj.ReadEvent(&event); exit {
+ return // exit
+ }
+ //dirty = false // these events don't invalidate state
+
+ case _ = <-TimeAfterOrBlock(obj.ctimeout):
+ obj.SetConvergedState(resConvergedTimeout)
+ obj.converged <- true
+ continue
+ }
+
+ // do all our event sending all together to avoid duplicate msgs
+ if send {
+ send = false
+ // only invalid state on certain types of events
+ if dirty {
+ dirty = false
+ obj.isStateOK = false // something made state dirty
+ }
+ Process(obj) // XXX: rename this function
+ }
+ }
+}
+
+func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
+ log.Printf("%v[%v]: CheckApply(%t)", obj.GetRes(), obj.GetName(), apply)
+
+ if obj.State == "" { // TODO: Validate() should replace this check!
+ log.Fatalf("%v[%v]: Package state is undefined!", obj.GetRes(), obj.GetName())
+ }
+
+ if obj.isStateOK { // cache the state
+ return true, nil
+ }
+
+ bus := NewBus()
+ if bus == nil {
+ return false, errors.New("Can't connect to PackageKit bus.")
+ }
+ defer bus.Close()
+
+ var packages = []string{obj.Name}
+ var filter uint64 = 0
+ filter += PK_FILTER_ENUM_ARCH // always search in our arch
+ // we're requesting latest version, or to narrow down install choices!
+ if obj.State == "newest" || obj.State == "installed" {
+ // if we add this, we'll still see older packages if installed
+ filter += PK_FILTER_ENUM_NEWEST // only search for newest packages
+ }
+ if !obj.AllowNonFree {
+ filter += PK_FILTER_ENUM_FREE
+ }
+ if !obj.AllowUnsupported {
+ filter += PK_FILTER_ENUM_SUPPORTED
+ }
+ if DEBUG {
+ log.Printf("Pkg[%v]: ResolvePackages: %v", obj.GetName(), strings.Join(packages, ", "))
+ }
+ resolved, e := bus.ResolvePackages(packages, filter)
+ if e != nil {
+ return false, errors.New(fmt.Sprintf("Resolve error: %v", e))
+ }
+
+ var found = false
+ var installed = false
+ var version = ""
+ var newest = true // assume, for now
+ var usePackageId = ""
+ for _, packageId := range resolved {
+ //log.Printf("* %v", packageId)
+ // format is: name;version;arch;data
+ s := strings.Split(packageId, ";")
+ //if len(s) != 4 { continue } // this would be a bug!
+ pkg, ver, _, data := s[0], s[1], s[2], s[3]
+ //arch := s[2] // TODO: double check match on arch?
+ if pkg != obj.Name { // not what we're looking for
+ continue
+ }
+ found = true
+ if obj.State != "installed" && obj.State != "uninstalled" && obj.State != "newest" { // must be a ver. string
+ if obj.State == ver && ver != "" { // we match what we want...
+ usePackageId = packageId
+ }
+ }
+ if FlagInData("installed", data) {
+ installed = true
+ version = ver
+ if obj.State == "uninstalled" {
+ usePackageId = packageId // save for later
+ }
+ } else { // not installed...
+ if obj.State == "installed" || obj.State == "newest" {
+ usePackageId = packageId
+ }
+ }
+
+ // if the first iteration didn't contain the installed package,
+ // then since the NEWEST filter was on, we're not the newest!
+ if !installed {
+ newest = false
+ }
+ }
+
+ // package doesn't exist, this is an error!
+ if !found {
+ return false, errors.New(fmt.Sprintf("Can't find package named '%s'.", obj.Name))
+ }
+
+ //obj.State == "installed" || "uninstalled" || "newest" || "4.2-1.fc23"
+ switch obj.State {
+ case "installed":
+ if installed {
+ return true, nil // state is correct, exit!
+ }
+ case "uninstalled":
+ if !installed {
+ return true, nil
+ }
+ case "newest":
+ if newest {
+ return true, nil
+ }
+ default: // version string
+ if obj.State == version && version != "" {
+ return true, nil
+ }
+ }
+
+ if usePackageId == "" {
+ return false, errors.New("Can't find package id to use.")
+ }
+
+ // state is not okay, no work done, exit, but without error
+ if !apply {
+ return false, nil
+ }
+
+ packageList := []string{usePackageId}
+ var transactionFlags uint64 = 0
+ if !obj.AllowUntrusted { // allow
+ transactionFlags += PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED
+ }
+ // apply correct state!
+ log.Printf("%v[%v]: Set: %v...", obj.GetRes(), obj.GetName(), obj.State)
+ switch obj.State {
+ case "uninstalled": // run remove
+ // NOTE: packageId is different than when installed, because now
+ // it has the "installed" flag added to the data portion if it!!
+ err = bus.RemovePackages(packageList, transactionFlags)
+
+ case "newest": // TODO: isn't this the same operation as install, below?
+ err = bus.UpdatePackages(packageList, transactionFlags)
+
+ case "installed":
+ fallthrough // same method as for "set specific version", below
+ default: // version string
+ err = bus.InstallPackages(packageList, transactionFlags)
+ }
+ if err != nil {
+ return false, err // fail
+ }
+ log.Printf("%v[%v]: Set: %v success!", obj.GetRes(), obj.GetName(), obj.State)
+ return false, nil // success
+}
+
+func (obj *PkgRes) Compare(res Res) bool {
+ switch res.(type) {
+ case *PkgRes:
+ res := res.(*PkgRes)
+ if obj.Name != res.Name {
+ return false
+ }
+ if obj.State != res.State {
+ return false
+ }
+ if obj.AllowUntrusted != res.AllowUntrusted {
+ return false
+ }
+ if obj.AllowNonFree != res.AllowNonFree {
+ return false
+ }
+ if obj.AllowUnsupported != res.AllowUnsupported {
+ return false
+ }
+ default:
+ return false
+ }
+ return true
+}
View
@@ -0,0 +1,52 @@
+---
+:domain: example.com
+:network: 192.168.123.0/24
+:image: centos-7.1
+:cpus: ''
+:memory: ''
+:disks: 0
+:disksize: 40G
+:boxurlprefix: ''
+:sync: rsync
+:syncdir: ''
+:syncsrc: ''
+:folder: ".omv"
+:extern:
+- type: git
+ repository: https://github.com/purpleidea/mgmt
+ directory: mgmt
+:cd: ''
+:puppet: false
+:classes: []
+:shell:
+- mkdir /tmp/mgmt/
+:docker: false
+:kubernetes: false
+:ansible: []
+:playbook: []
+:ansible_extras: {}
+:cachier: false
+:vms:
+- :name: mgmt1
+ :shell:
+ - iptables -F
+ - cd /vagrant/mgmt/ && make path
+ - cd /vagrant/mgmt/ && make deps && make build && cp mgmt ~/bin/
+ - etcd -bind-addr "`hostname --ip-address`:2379" &
+ - cd && mgmt run --file /vagrant/mgmt/examples/pkg1.yaml --converged-timeout=5
+:namespace: omv
+:count: 0
+:username: ''
+:password: ''
+:poolid: true
+:repos: []
+:update: false
+:reboot: false
+:unsafe: false
+:nested: false
+:tests:
+- omv up
+- vssh root@mgmt1 -c which powertop
+- omv destroy
+:comment: simple package install test case
+:reallyrm: false
View
@@ -0,0 +1,52 @@
+---
+:domain: example.com
+:network: 192.168.123.0/24
+:image: debian-8
+:cpus: ''
+:memory: ''
+:disks: 0
+:disksize: 40G
+:boxurlprefix: ''
+:sync: rsync
+:syncdir: ''
+:syncsrc: ''
+:folder: ".omv"
+:extern:
+- type: git
+ repository: https://github.com/purpleidea/mgmt
+ directory: mgmt
+:cd: ''
+:puppet: false
+:classes: []
+:shell:
+- mkdir /tmp/mgmt/
+:docker: false
+:kubernetes: false
+:ansible: []
+:playbook: []
+:ansible_extras: {}
+:cachier: false
+:vms:
+- :name: mgmt1
+ :shell:
+ - iptables -F
+ - cd /vagrant/mgmt/ && make path
+ - cd /vagrant/mgmt/ && make deps && make build && cp mgmt ~/bin/
+ - etcd -bind-addr "`hostname --ip-address`:2379" &
+ - cd && mgmt run --file /vagrant/mgmt/examples/pkg1.yaml --converged-timeout=5
+:namespace: omv
+:count: 0
+:username: ''
+:password: ''
+:poolid: true
+:repos: []
+:update: false
+:reboot: false
+:unsafe: false
+:nested: false
+:tests:
+- omv up
+- vssh root@mgmt1 -c which powertop
+- omv destroy
+:comment: simple package install test case
+:reallyrm: false
View
@@ -6,7 +6,10 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # dir!
cd "$DIR" >/dev/null # work from test directory
# vtest+ tests
-vtest+ omv/helloworld.yaml
+for i in omv/*.yaml; do
+ echo "running: vtest+ $i"
+ vtest+ "$i"
+done
# return to original dir
cd "$CWD" >/dev/null