From 28fdf342ff2008367043c2bc00a42f7394747121 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 23 Mar 2016 05:00:34 +0000 Subject: [PATCH 1/2] provider/cobbler: Cobbler Provider This introduces a provider for Cobbler. Cobbler manages bare-metal deployments and, to some extent, virtual machines. This initial commit supports the following resources: distros, profiles, systems, kickstart files, and snippets. --- builtin/bins/provider-cobbler/main.go | 12 + .../cobbler/acceptance_env/deploy.sh | 86 ++ builtin/providers/cobbler/config.go | 33 + builtin/providers/cobbler/provider.go | 76 ++ builtin/providers/cobbler/provider_test.go | 46 + .../cobbler/resource_cobbler_distro.go | 224 +++++ .../cobbler/resource_cobbler_distro_test.go | 129 +++ .../resource_cobbler_kickstart_file.go | 83 ++ .../resource_cobbler_kickstart_file_test.go | 79 ++ .../cobbler/resource_cobbler_profile.go | 369 ++++++++ .../cobbler/resource_cobbler_profile_test.go | 149 +++ .../cobbler/resource_cobbler_snippet.go | 83 ++ .../cobbler/resource_cobbler_snippet_test.go | 79 ++ .../cobbler/resource_cobbler_system.go | 851 ++++++++++++++++++ .../cobbler/resource_cobbler_system_test.go | 380 ++++++++ .../providers/cobbler/index.html.markdown | 45 + .../providers/cobbler/r/distro.html.markdown | 84 ++ .../cobbler/r/kickstart_file.html.markdown | 29 + .../providers/cobbler/r/profile.html.markdown | 92 ++ .../providers/cobbler/r/snippet.html.markdown | 29 + .../providers/cobbler/r/system.html.markdown | 189 ++++ website/source/layouts/cobbler.erb | 38 + website/source/layouts/docs.erb | 6 +- 23 files changed, 3190 insertions(+), 1 deletion(-) create mode 100644 builtin/bins/provider-cobbler/main.go create mode 100644 builtin/providers/cobbler/acceptance_env/deploy.sh create mode 100644 builtin/providers/cobbler/config.go create mode 100644 builtin/providers/cobbler/provider.go create mode 100644 builtin/providers/cobbler/provider_test.go create mode 100644 builtin/providers/cobbler/resource_cobbler_distro.go create mode 100644 builtin/providers/cobbler/resource_cobbler_distro_test.go create mode 100644 builtin/providers/cobbler/resource_cobbler_kickstart_file.go create mode 100644 builtin/providers/cobbler/resource_cobbler_kickstart_file_test.go create mode 100644 builtin/providers/cobbler/resource_cobbler_profile.go create mode 100644 builtin/providers/cobbler/resource_cobbler_profile_test.go create mode 100644 builtin/providers/cobbler/resource_cobbler_snippet.go create mode 100644 builtin/providers/cobbler/resource_cobbler_snippet_test.go create mode 100644 builtin/providers/cobbler/resource_cobbler_system.go create mode 100644 builtin/providers/cobbler/resource_cobbler_system_test.go create mode 100644 website/source/docs/providers/cobbler/index.html.markdown create mode 100644 website/source/docs/providers/cobbler/r/distro.html.markdown create mode 100644 website/source/docs/providers/cobbler/r/kickstart_file.html.markdown create mode 100644 website/source/docs/providers/cobbler/r/profile.html.markdown create mode 100644 website/source/docs/providers/cobbler/r/snippet.html.markdown create mode 100644 website/source/docs/providers/cobbler/r/system.html.markdown create mode 100644 website/source/layouts/cobbler.erb diff --git a/builtin/bins/provider-cobbler/main.go b/builtin/bins/provider-cobbler/main.go new file mode 100644 index 000000000000..73d46b96ee20 --- /dev/null +++ b/builtin/bins/provider-cobbler/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/cobbler" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: cobbler.Provider, + }) +} diff --git a/builtin/providers/cobbler/acceptance_env/deploy.sh b/builtin/providers/cobbler/acceptance_env/deploy.sh new file mode 100644 index 000000000000..09742d84dcde --- /dev/null +++ b/builtin/providers/cobbler/acceptance_env/deploy.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +# This script assumes Ubuntu 14.04 is being used. +# It will create a standard Cobbler environment that can be used for acceptance testing. + +sudo apt-get update +sudo apt-get install -y git make mercurial + +cd +echo 'export PATH=$PATH:$HOME/terraform:$HOME/go/bin' >> ~/.bashrc +export PATH=$PATH:$HOME/terraform:$HOME/go/bin + +sudo wget -O /usr/local/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme +sudo chmod +x /usr/local/bin/gimme +/usr/local/bin/gimme 1.6 >> ~/.bashrc +eval "$(/usr/local/bin/gimme 1.6)" + +mkdir ~/go +echo 'export GOPATH=$HOME/go' >> ~/.bashrc +echo 'export GO15VENDOREXPERIMENT=1' >> ~/.bashrc +export GOPATH=$HOME/go +source ~/.bashrc + +go get github.com/tools/godep +go get github.com/hashicorp/terraform +cd $GOPATH/src/github.com/hashicorp/terraform +godep restore + +# Cobbler +sudo apt-get install -y cobbler cobbler-web debmirror dnsmasq + +sudo tee /etc/cobbler/modules.conf < + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> + <% end %> diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index f1ec04584c7b..4ad58f5be277 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -161,6 +161,10 @@ CloudStack + > + Cobbler + + > Consul @@ -252,7 +256,7 @@ > TLS - + > Triton From 7370054451417ce1089745e6c8fd90b4fb04790e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Mar 2016 15:50:07 +0000 Subject: [PATCH 2/2] vendor: Capturing dependencies for cobbler provider --- Godeps/Godeps.json | 14 + vendor/github.com/fatih/structs/.gitignore | 23 + vendor/github.com/fatih/structs/.travis.yml | 11 + vendor/github.com/fatih/structs/LICENSE | 21 + vendor/github.com/fatih/structs/README.md | 163 ++ vendor/github.com/fatih/structs/field.go | 133 ++ vendor/github.com/fatih/structs/structs.go | 494 ++++ vendor/github.com/fatih/structs/tags.go | 32 + .../jtopjian/cobblerclient/.gitignore | 1 + .../github.com/jtopjian/cobblerclient/LICENSE | 202 ++ .../jtopjian/cobblerclient/Makefile | 20 + .../jtopjian/cobblerclient/README.md | 2 + .../jtopjian/cobblerclient/cobblerclient.go | 198 ++ .../jtopjian/cobblerclient/distro.go | 145 ++ .../jtopjian/cobblerclient/kickstart_file.go | 56 + .../jtopjian/cobblerclient/methods.txt | 1990 +++++++++++++++++ .../jtopjian/cobblerclient/profile.go | 230 ++ .../jtopjian/cobblerclient/snippet.go | 56 + .../jtopjian/cobblerclient/system.go | 336 +++ vendor/github.com/kolo/xmlrpc/LICENSE | 19 + vendor/github.com/kolo/xmlrpc/README.md | 79 + vendor/github.com/kolo/xmlrpc/client.go | 144 ++ vendor/github.com/kolo/xmlrpc/decoder.go | 449 ++++ vendor/github.com/kolo/xmlrpc/encoder.go | 164 ++ vendor/github.com/kolo/xmlrpc/request.go | 57 + vendor/github.com/kolo/xmlrpc/response.go | 52 + vendor/github.com/kolo/xmlrpc/test_server.rb | 25 + vendor/github.com/kolo/xmlrpc/xmlrpc.go | 19 + 28 files changed, 5135 insertions(+) create mode 100644 vendor/github.com/fatih/structs/.gitignore create mode 100644 vendor/github.com/fatih/structs/.travis.yml create mode 100644 vendor/github.com/fatih/structs/LICENSE create mode 100644 vendor/github.com/fatih/structs/README.md create mode 100644 vendor/github.com/fatih/structs/field.go create mode 100644 vendor/github.com/fatih/structs/structs.go create mode 100644 vendor/github.com/fatih/structs/tags.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/.gitignore create mode 100644 vendor/github.com/jtopjian/cobblerclient/LICENSE create mode 100644 vendor/github.com/jtopjian/cobblerclient/Makefile create mode 100644 vendor/github.com/jtopjian/cobblerclient/README.md create mode 100644 vendor/github.com/jtopjian/cobblerclient/cobblerclient.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/distro.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/kickstart_file.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/methods.txt create mode 100644 vendor/github.com/jtopjian/cobblerclient/profile.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/snippet.go create mode 100644 vendor/github.com/jtopjian/cobblerclient/system.go create mode 100644 vendor/github.com/kolo/xmlrpc/LICENSE create mode 100644 vendor/github.com/kolo/xmlrpc/README.md create mode 100644 vendor/github.com/kolo/xmlrpc/client.go create mode 100644 vendor/github.com/kolo/xmlrpc/decoder.go create mode 100644 vendor/github.com/kolo/xmlrpc/encoder.go create mode 100644 vendor/github.com/kolo/xmlrpc/request.go create mode 100644 vendor/github.com/kolo/xmlrpc/response.go create mode 100644 vendor/github.com/kolo/xmlrpc/test_server.rb create mode 100644 vendor/github.com/kolo/xmlrpc/xmlrpc.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 885d040c03a4..6b15408dc48b 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,7 @@ { "ImportPath": "github.com/hashicorp/terraform", "GoVersion": "go1.6", + "GodepVersion": "v60", "Packages": [ "./..." ], @@ -559,6 +560,10 @@ "ImportPath": "github.com/dylanmei/winrmtest", "Rev": "025617847eb2cf9bd1d851bc3b22ed28e6245ce5" }, + { + "ImportPath": "github.com/fatih/structs", + "Rev": "73c4e3dc02a78deaba8640d5f3a8c236ec1352bf" + }, { "ImportPath": "github.com/fsouza/go-dockerclient", "Rev": "02a8beb401b20e112cff3ea740545960b667eab1" @@ -840,10 +845,19 @@ "ImportPath": "github.com/joyent/gosign/auth", "Rev": "a1f3aa7d52213987117e47d721bcc9a499994d5f" }, + { + "ImportPath": "github.com/jtopjian/cobblerclient", + "Comment": "v0.3.0-33-g53d1c0a", + "Rev": "53d1c0a0b003aabfa7ecfa848d856606cb481196" + }, { "ImportPath": "github.com/kardianos/osext", "Rev": "29ae4ffbc9a6fe9fb2bc5029050ce6996ea1d3bc" }, + { + "ImportPath": "github.com/kolo/xmlrpc", + "Rev": "0826b98aaa29c0766956cb40d45cf7482a597671" + }, { "ImportPath": "github.com/lib/pq", "Comment": "go1.0-cutoff-74-g8ad2b29", diff --git a/vendor/github.com/fatih/structs/.gitignore b/vendor/github.com/fatih/structs/.gitignore new file mode 100644 index 000000000000..836562412fe8 --- /dev/null +++ b/vendor/github.com/fatih/structs/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/fatih/structs/.travis.yml b/vendor/github.com/fatih/structs/.travis.yml new file mode 100644 index 000000000000..845012b7ab0e --- /dev/null +++ b/vendor/github.com/fatih/structs/.travis.yml @@ -0,0 +1,11 @@ +language: go +go: + - 1.6 + - tip +sudo: false +before_install: +- go get github.com/axw/gocov/gocov +- go get github.com/mattn/goveralls +- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi +script: +- $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/fatih/structs/LICENSE b/vendor/github.com/fatih/structs/LICENSE new file mode 100644 index 000000000000..34504e4b3efb --- /dev/null +++ b/vendor/github.com/fatih/structs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Fatih Arslan + +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. \ No newline at end of file diff --git a/vendor/github.com/fatih/structs/README.md b/vendor/github.com/fatih/structs/README.md new file mode 100644 index 000000000000..44e01006e135 --- /dev/null +++ b/vendor/github.com/fatih/structs/README.md @@ -0,0 +1,163 @@ +# Structs [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structs) [![Build Status](http://img.shields.io/travis/fatih/structs.svg?style=flat-square)](https://travis-ci.org/fatih/structs) [![Coverage Status](http://img.shields.io/coveralls/fatih/structs.svg?style=flat-square)](https://coveralls.io/r/fatih/structs) + +Structs contains various utilities to work with Go (Golang) structs. It was +initially used by me to convert a struct into a `map[string]interface{}`. With +time I've added other utilities for structs. It's basically a high level +package based on primitives from the reflect package. Feel free to add new +functions or improve the existing code. + +## Install + +```bash +go get github.com/fatih/structs +``` + +## Usage and Examples + +Just like the standard lib `strings`, `bytes` and co packages, `structs` has +many global functions to manipulate or organize your struct data. Lets define +and declare a struct: + +```go +type Server struct { + Name string `json:"name,omitempty"` + ID int + Enabled bool + users []string // not exported + http.Server // embedded +} + +server := &Server{ + Name: "gopher", + ID: 123456, + Enabled: true, +} +``` + +```go +// Convert a struct to a map[string]interface{} +// => {"Name":"gopher", "ID":123456, "Enabled":true} +m := structs.Map(server) + +// Convert the values of a struct to a []interface{} +// => ["gopher", 123456, true] +v := structs.Values(server) + +// Convert the names of a struct to a []string +// (see "Names methods" for more info about fields) +n := structs.Names(server) + +// Convert the values of a struct to a []*Field +// (see "Field methods" for more info about fields) +f := structs.Fields(server) + +// Return the struct name => "Server" +n := structs.Name(server) + +// Check if any field of a struct is initialized or not. +h := structs.HasZero(server) + +// Check if all fields of a struct is initialized or not. +z := structs.IsZero(server) + +// Check if server is a struct or a pointer to struct +i := structs.IsStruct(server) +``` + +### Struct methods + +The structs functions can be also used as independent methods by creating a new +`*structs.Struct`. This is handy if you want to have more control over the +structs (such as retrieving a single Field). + +```go +// Create a new struct type: +s := structs.New(server) + +m := s.Map() // Get a map[string]interface{} +v := s.Values() // Get a []interface{} +f := s.Fields() // Get a []*Field +n := s.Names() // Get a []string +f := s.Field(name) // Get a *Field based on the given field name +f, ok := s.FieldOk(name) // Get a *Field based on the given field name +n := s.Name() // Get the struct name +h := s.HasZero() // Check if any field is initialized +z := s.IsZero() // Check if all fields are initialized +``` + +### Field methods + +We can easily examine a single Field for more detail. Below you can see how we +get and interact with various field methods: + + +```go +s := structs.New(server) + +// Get the Field struct for the "Name" field +name := s.Field("Name") + +// Get the underlying value, value => "gopher" +value := name.Value().(string) + +// Set the field's value +name.Set("another gopher") + +// Get the field's kind, kind => "string" +name.Kind() + +// Check if the field is exported or not +if name.IsExported() { + fmt.Println("Name field is exported") +} + +// Check if the value is a zero value, such as "" for string, 0 for int +if !name.IsZero() { + fmt.Println("Name is initialized") +} + +// Check if the field is an anonymous (embedded) field +if !name.IsEmbedded() { + fmt.Println("Name is not an embedded field") +} + +// Get the Field's tag value for tag name "json", tag value => "name,omitempty" +tagValue := name.Tag("json") +``` + +Nested structs are supported too: + +```go +addrField := s.Field("Server").Field("Addr") + +// Get the value for addr +a := addrField.Value().(string) + +// Or get all fields +httpServer := s.Field("Server").Fields() +``` + +We can also get a slice of Fields from the Struct type to iterate over all +fields. This is handy if you wish to examine all fields: + +```go +s := structs.New(server) + +for _, f := range s.Fields() { + fmt.Printf("field name: %+v\n", f.Name()) + + if f.IsExported() { + fmt.Printf("value : %+v\n", f.Value()) + fmt.Printf("is zero : %+v\n", f.IsZero()) + } +} +``` + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * [Cihangir Savas](https://github.com/cihangir) + +## License + +The MIT License (MIT) - see LICENSE.md for more details diff --git a/vendor/github.com/fatih/structs/field.go b/vendor/github.com/fatih/structs/field.go new file mode 100644 index 000000000000..566f5497ecbb --- /dev/null +++ b/vendor/github.com/fatih/structs/field.go @@ -0,0 +1,133 @@ +package structs + +import ( + "errors" + "fmt" + "reflect" +) + +var ( + errNotExported = errors.New("field is not exported") + errNotSettable = errors.New("field is not settable") +) + +// Field represents a single struct field that encapsulates high level +// functions around the field. +type Field struct { + value reflect.Value + field reflect.StructField + defaultTag string +} + +// Tag returns the value associated with key in the tag string. If there is no +// such key in the tag, Tag returns the empty string. +func (f *Field) Tag(key string) string { + return f.field.Tag.Get(key) +} + +// Value returns the underlying value of of the field. It panics if the field +// is not exported. +func (f *Field) Value() interface{} { + return f.value.Interface() +} + +// IsEmbedded returns true if the given field is an anonymous field (embedded) +func (f *Field) IsEmbedded() bool { + return f.field.Anonymous +} + +// IsExported returns true if the given field is exported. +func (f *Field) IsExported() bool { + return f.field.PkgPath == "" +} + +// IsZero returns true if the given field is not initialized (has a zero value). +// It panics if the field is not exported. +func (f *Field) IsZero() bool { + zero := reflect.Zero(f.value.Type()).Interface() + current := f.Value() + + return reflect.DeepEqual(current, zero) +} + +// Name returns the name of the given field +func (f *Field) Name() string { + return f.field.Name +} + +// Kind returns the fields kind, such as "string", "map", "bool", etc .. +func (f *Field) Kind() reflect.Kind { + return f.value.Kind() +} + +// Set sets the field to given value v. It returns an error if the field is not +// settable (not addressable or not exported) or if the given value's type +// doesn't match the fields type. +func (f *Field) Set(val interface{}) error { + // we can't set unexported fields, so be sure this field is exported + if !f.IsExported() { + return errNotExported + } + + // do we get here? not sure... + if !f.value.CanSet() { + return errNotSettable + } + + given := reflect.ValueOf(val) + + if f.value.Kind() != given.Kind() { + return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind()) + } + + f.value.Set(given) + return nil +} + +// Zero sets the field to its zero value. It returns an error if the field is not +// settable (not addressable or not exported). +func (f *Field) Zero() error { + zero := reflect.Zero(f.value.Type()).Interface() + return f.Set(zero) +} + +// Fields returns a slice of Fields. This is particular handy to get the fields +// of a nested struct . A struct tag with the content of "-" ignores the +// checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field *http.Request `structs:"-"` +// +// It panics if field is not exported or if field's kind is not struct +func (f *Field) Fields() []*Field { + return getFields(f.value, f.defaultTag) +} + +// Field returns the field from a nested struct. It panics if the nested struct +// is not exported or if the field was not found. +func (f *Field) Field(name string) *Field { + field, ok := f.FieldOk(name) + if !ok { + panic("field not found") + } + + return field +} + +// Field returns the field from a nested struct. The boolean returns true if +// the field was found. It panics if the nested struct is not exported or if +// the field was not found. +func (f *Field) FieldOk(name string) (*Field, bool) { + v := strctVal(f.value.Interface()) + t := v.Type() + + field, ok := t.FieldByName(name) + if !ok { + return nil, false + } + + return &Field{ + field: field, + value: v.FieldByName(name), + }, true +} diff --git a/vendor/github.com/fatih/structs/structs.go b/vendor/github.com/fatih/structs/structs.go new file mode 100644 index 000000000000..408d50f2839b --- /dev/null +++ b/vendor/github.com/fatih/structs/structs.go @@ -0,0 +1,494 @@ +// Package structs contains various utilities functions to work with structs. +package structs + +import ( + "fmt" + + "reflect" +) + +var ( + // DefaultTagName is the default tag name for struct fields which provides + // a more granular to tweak certain structs. Lookup the necessary functions + // for more info. + DefaultTagName = "structs" // struct's field default tag name +) + +// Struct encapsulates a struct type to provide several high level functions +// around the struct. +type Struct struct { + raw interface{} + value reflect.Value + TagName string +} + +// New returns a new *Struct with the struct s. It panics if the s's kind is +// not struct. +func New(s interface{}) *Struct { + return &Struct{ + raw: s, + value: strctVal(s), + TagName: DefaultTagName, + } +} + +// Map converts the given struct to a map[string]interface{}, where the keys +// of the map are the field names and the values of the map the associated +// values of the fields. The default key string is the struct field name but +// can be changed in the struct field's tag value. The "structs" key in the +// struct's field tag value is the key name. Example: +// +// // Field appears in map as key "myName". +// Name string `structs:"myName"` +// +// A tag value with the content of "-" ignores that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A tag value with the content of "string" uses the stringer to get the value. Example: +// +// // The value will be output of Animal's String() func. +// // Map will panic if Animal does not implement String(). +// Field *Animal `structs:"field,string"` +// +// A tag value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// A tag value with the option of "omitempty" ignores that particular field if +// the field value is empty. Example: +// +// // Field appears in map as key "myName", but the field is +// // skipped if empty. +// Field string `structs:"myName,omitempty"` +// +// // Field appears in map as key "Field" (the default), but +// // the field is skipped if empty. +// Field string `structs:",omitempty"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. +func (s *Struct) Map() map[string]interface{} { + out := make(map[string]interface{}) + s.FillMap(out) + return out +} + +// FillMap is the same as Map. Instead of returning the output, it fills the +// given map. +func (s *Struct) FillMap(out map[string]interface{}) { + if out == nil { + return + } + + fields := s.structFields() + + for _, field := range fields { + name := field.Name + val := s.value.FieldByName(name) + + var finalVal interface{} + + tagName, tagOpts := parseTag(field.Tag.Get(s.TagName)) + if tagName != "" { + name = tagName + } + + // if the value is a zero value and the field is marked as omitempty do + // not include + if tagOpts.Has("omitempty") { + zero := reflect.Zero(val.Type()).Interface() + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + continue + } + } + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + // look out for embedded structs, and convert them to a + // map[string]interface{} too + n := New(val.Interface()) + n.TagName = s.TagName + m := n.Map() + if len(m) == 0 { + finalVal = val.Interface() + } else { + finalVal = m + } + } else { + finalVal = val.Interface() + } + + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + out[name] = s.String() + } + continue + } + + out[name] = finalVal + } +} + +// Values converts the given s struct's field values to a []interface{}. A +// struct tag with the content of "-" ignores the that particular field. +// Example: +// +// // Field is ignored by this package. +// Field int `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Fields is not processed further by this package. +// Field time.Time `structs:",omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// A tag value with the option of "omitempty" ignores that particular field and +// is not added to the values if the field value is empty. Example: +// +// // Field is skipped if empty +// Field string `structs:",omitempty"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. +func (s *Struct) Values() []interface{} { + fields := s.structFields() + + var t []interface{} + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + // if the value is a zero value and the field is marked as omitempty do + // not include + if tagOpts.Has("omitempty") { + zero := reflect.Zero(val.Type()).Interface() + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + continue + } + } + + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + t = append(t, s.String()) + } + continue + } + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + // look out for embedded structs, and convert them to a + // []interface{} to be added to the final values slice + for _, embeddedVal := range Values(val.Interface()) { + t = append(t, embeddedVal) + } + } else { + t = append(t, val.Interface()) + } + } + + return t +} + +// Fields returns a slice of Fields. A struct tag with the content of "-" +// ignores the checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// It panics if s's kind is not struct. +func (s *Struct) Fields() []*Field { + return getFields(s.value, s.TagName) +} + +// Names returns a slice of field names. A struct tag with the content of "-" +// ignores the checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// It panics if s's kind is not struct. +func (s *Struct) Names() []string { + fields := getFields(s.value, s.TagName) + + names := make([]string, len(fields)) + + for i, field := range fields { + names[i] = field.Name() + } + + return names +} + +func getFields(v reflect.Value, tagName string) []*Field { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + t := v.Type() + + var fields []*Field + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + + if tag := field.Tag.Get(tagName); tag == "-" { + continue + } + + f := &Field{ + field: field, + value: v.FieldByName(field.Name), + } + + fields = append(fields, f) + + } + + return fields +} + +// Field returns a new Field struct that provides several high level functions +// around a single struct field entity. It panics if the field is not found. +func (s *Struct) Field(name string) *Field { + f, ok := s.FieldOk(name) + if !ok { + panic("field not found") + } + + return f +} + +// Field returns a new Field struct that provides several high level functions +// around a single struct field entity. The boolean returns true if the field +// was found. +func (s *Struct) FieldOk(name string) (*Field, bool) { + t := s.value.Type() + + field, ok := t.FieldByName(name) + if !ok { + return nil, false + } + + return &Field{ + field: field, + value: s.value.FieldByName(name), + defaultTag: s.TagName, + }, true +} + +// IsZero returns true if all fields in a struct is a zero value (not +// initialized) A struct tag with the content of "-" ignores the checking of +// that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. It panics if s's kind is not struct. +func (s *Struct) IsZero() bool { + fields := s.structFields() + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + ok := IsZero(val.Interface()) + if !ok { + return false + } + + continue + } + + // zero value of the given field, such as "" for string, 0 for int + zero := reflect.Zero(val.Type()).Interface() + + // current value of the given field + current := val.Interface() + + if !reflect.DeepEqual(current, zero) { + return false + } + } + + return true +} + +// HasZero returns true if a field in a struct is not initialized (zero value). +// A struct tag with the content of "-" ignores the checking of that particular +// field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. It panics if s's kind is not struct. +func (s *Struct) HasZero() bool { + fields := s.structFields() + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + ok := HasZero(val.Interface()) + if ok { + return true + } + + continue + } + + // zero value of the given field, such as "" for string, 0 for int + zero := reflect.Zero(val.Type()).Interface() + + // current value of the given field + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + return true + } + } + + return false +} + +// Name returns the structs's type name within its package. For more info refer +// to Name() function. +func (s *Struct) Name() string { + return s.value.Type().Name() +} + +// structFields returns the exported struct fields for a given s struct. This +// is a convenient helper method to avoid duplicate code in some of the +// functions. +func (s *Struct) structFields() []reflect.StructField { + t := s.value.Type() + + var f []reflect.StructField + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + // we can't access the value of unexported fields + if field.PkgPath != "" { + continue + } + + // don't check if it's omitted + if tag := field.Tag.Get(s.TagName); tag == "-" { + continue + } + + f = append(f, field) + } + + return f +} + +func strctVal(s interface{}) reflect.Value { + v := reflect.ValueOf(s) + + // if pointer get the underlying element≤ + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + panic("not struct") + } + + return v +} + +// Map converts the given struct to a map[string]interface{}. For more info +// refer to Struct types Map() method. It panics if s's kind is not struct. +func Map(s interface{}) map[string]interface{} { + return New(s).Map() +} + +// FillMap is the same as Map. Instead of returning the output, it fills the +// given map. +func FillMap(s interface{}, out map[string]interface{}) { + New(s).FillMap(out) +} + +// Values converts the given struct to a []interface{}. For more info refer to +// Struct types Values() method. It panics if s's kind is not struct. +func Values(s interface{}) []interface{} { + return New(s).Values() +} + +// Fields returns a slice of *Field. For more info refer to Struct types +// Fields() method. It panics if s's kind is not struct. +func Fields(s interface{}) []*Field { + return New(s).Fields() +} + +// Names returns a slice of field names. For more info refer to Struct types +// Names() method. It panics if s's kind is not struct. +func Names(s interface{}) []string { + return New(s).Names() +} + +// IsZero returns true if all fields is equal to a zero value. For more info +// refer to Struct types IsZero() method. It panics if s's kind is not struct. +func IsZero(s interface{}) bool { + return New(s).IsZero() +} + +// HasZero returns true if any field is equal to a zero value. For more info +// refer to Struct types HasZero() method. It panics if s's kind is not struct. +func HasZero(s interface{}) bool { + return New(s).HasZero() +} + +// IsStruct returns true if the given variable is a struct or a pointer to +// struct. +func IsStruct(s interface{}) bool { + v := reflect.ValueOf(s) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + // uninitialized zero value of a struct + if v.Kind() == reflect.Invalid { + return false + } + + return v.Kind() == reflect.Struct +} + +// Name returns the structs's type name within its package. It returns an +// empty string for unnamed types. It panics if s's kind is not struct. +func Name(s interface{}) string { + return New(s).Name() +} diff --git a/vendor/github.com/fatih/structs/tags.go b/vendor/github.com/fatih/structs/tags.go new file mode 100644 index 000000000000..8859341c1f9b --- /dev/null +++ b/vendor/github.com/fatih/structs/tags.go @@ -0,0 +1,32 @@ +package structs + +import "strings" + +// tagOptions contains a slice of tag options +type tagOptions []string + +// Has returns true if the given optiton is available in tagOptions +func (t tagOptions) Has(opt string) bool { + for _, tagOpt := range t { + if tagOpt == opt { + return true + } + } + + return false +} + +// parseTag splits a struct field's tag into its name and a list of options +// which comes after a name. A tag is in the form of: "name,option1,option2". +// The name can be neglectected. +func parseTag(tag string) (string, tagOptions) { + // tag is one of followings: + // "" + // "name" + // "name,opt" + // "name,opt,opt2" + // ",opt" + + res := strings.Split(tag, ",") + return res[0], res[1:] +} diff --git a/vendor/github.com/jtopjian/cobblerclient/.gitignore b/vendor/github.com/jtopjian/cobblerclient/.gitignore new file mode 100644 index 000000000000..ead84456eb0e --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/.gitignore @@ -0,0 +1 @@ +**/*.swp diff --git a/vendor/github.com/jtopjian/cobblerclient/LICENSE b/vendor/github.com/jtopjian/cobblerclient/LICENSE new file mode 100644 index 000000000000..8f71f43fee3f --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/vendor/github.com/jtopjian/cobblerclient/Makefile b/vendor/github.com/jtopjian/cobblerclient/Makefile new file mode 100644 index 000000000000..a69df5699a5e --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/Makefile @@ -0,0 +1,20 @@ +# Copyright 2015 Container Solutions +# +# 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. + + +build: + @go build . + +test: + @go test -v . diff --git a/vendor/github.com/jtopjian/cobblerclient/README.md b/vendor/github.com/jtopjian/cobblerclient/README.md new file mode 100644 index 000000000000..283252d480cf --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/README.md @@ -0,0 +1,2 @@ +# cobblerclient +Cobbler Client written in Go diff --git a/vendor/github.com/jtopjian/cobblerclient/cobblerclient.go b/vendor/github.com/jtopjian/cobblerclient/cobblerclient.go new file mode 100644 index 000000000000..7383eee821ea --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/cobblerclient.go @@ -0,0 +1,198 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strings" + + "github.com/kolo/xmlrpc" + "github.com/mitchellh/mapstructure" +) + +const bodyTypeXML = "text/xml" + +type HTTPClient interface { + Post(string, string, io.Reader) (*http.Response, error) +} + +type Client struct { + httpClient HTTPClient + config ClientConfig + Token string +} + +type ClientConfig struct { + Url string + Username string + Password string +} + +func NewClient(httpClient HTTPClient, c ClientConfig) Client { + return Client{ + httpClient: httpClient, + config: c, + } +} + +func (c *Client) Call(method string, args ...interface{}) (interface{}, error) { + var result interface{} + + reqBody, err := xmlrpc.EncodeMethodCall(method, args...) + if err != nil { + return nil, err + } + + r := fmt.Sprintf("%s\n", string(reqBody)) + res, err := c.httpClient.Post(c.config.Url, bodyTypeXML, bytes.NewReader([]byte(r))) + if err != nil { + return nil, err + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + resp := xmlrpc.NewResponse(body) + if err := resp.Unmarshal(&result); err != nil { + return nil, err + } + + if resp.Failed() { + return nil, resp.Err() + } + + return result, nil +} + +// Performs a login request to Cobbler using the credentials provided +// in the configuration in the initializer. +func (c *Client) Login() (bool, error) { + result, err := c.Call("login", c.config.Username, c.config.Password) + if err != nil { + return false, err + } + + c.Token = result.(string) + return true, nil +} + +// Sync the system. +// Returns an error if anything went wrong +func (c *Client) Sync() error { + _, err := c.Call("sync", c.Token) + return err +} + +// GetItemHandle gets the internal ID of a Cobbler item. +func (c *Client) GetItemHandle(what, name string) (string, error) { + result, err := c.Call("get_item_handle", what, name, c.Token) + if err != nil { + return "", err + } else { + return result.(string), err + } +} + +// cobblerDataHacks is a hook for the mapstructure decoder. It's only used by +// decodeCobblerItem and should never be invoked directly. +// It's used to smooth out issues with converting fields and types from Cobbler. +func cobblerDataHacks(f, t reflect.Kind, data interface{}) (interface{}, error) { + dataVal := reflect.ValueOf(data) + + // Cobbler uses ~ internally to mean None/nil + if dataVal.String() == "~" { + return map[string]interface{}{}, nil + } + + if f == reflect.Int64 && t == reflect.Bool { + if dataVal.Int() > 0 { + return true, nil + } else { + return false, nil + } + } + return data, nil +} + +// decodeCobblerItem is a custom mapstructure decoder to handler Cobbler's uniqueness. +func decodeCobblerItem(raw interface{}, result interface{}) (interface{}, error) { + var metadata mapstructure.Metadata + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Metadata: &metadata, + Result: result, + WeaklyTypedInput: true, + DecodeHook: cobblerDataHacks, + }) + + if err != nil { + return nil, err + } + + if err := decoder.Decode(raw); err != nil { + return nil, err + } + + return result, nil +} + +// updateCobblerFields updates all fields in a Cobbler Item structure. +func (c *Client) updateCobblerFields(what string, item reflect.Value, id string) error { + method := fmt.Sprintf("modify_%s", what) + + typeOfT := item.Type() + for i := 0; i < item.NumField(); i++ { + v := item.Field(i) + tag := typeOfT.Field(i).Tag + field := tag.Get("mapstructure") + cobblerTag := tag.Get("cobbler") + + if cobblerTag == "noupdate" { + continue + } + + if field == "" { + continue + } + + var value interface{} + switch v.Type().String() { + case "string", "bool", "int64", "int": + value = v.Interface() + case "[]string": + value = strings.Join(v.Interface().([]string), " ") + } + + //fmt.Printf("%s, %s, %s\n", id, field, value) + if result, err := c.Call(method, id, field, value, c.Token); err != nil { + return err + } else { + if result.(bool) == false && value != false { + return fmt.Errorf("Error updating %s to %s.", field, value) + } + } + } + + return nil +} diff --git a/vendor/github.com/jtopjian/cobblerclient/distro.go b/vendor/github.com/jtopjian/cobblerclient/distro.go new file mode 100644 index 000000000000..12ad5698999f --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/distro.go @@ -0,0 +1,145 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +import ( + "fmt" + "reflect" +) + +// Distro is a created distro. +type Distro struct { + // These are internal fields and cannot be modified. + Ctime float64 `mapstructure:"ctime" cobbler:"noupdate"` // TODO: convert to time + Depth int `mapstructure:"depth" cobbler:"noupdate"` + ID string `mapstructure:"uid" cobbler:"noupdate"` + Mtime float64 `mapstructure:"mtime" cobbler:"noupdate"` // TODO: convert to time + TreeBuildTime string `mapstructure:tree_build_time" cobbler:"noupdate"` + + Arch string `mapstructure:"arch"` + Breed string `mapstructure:"breed"` + BootFiles string `mapstructure:"boot_files"` + Comment string `mapstructure:"comment"` + FetchableFiles string `mapstructure:"fetchable_files"` + Kernel string `mapstructure:"kernel"` + KernelOptions string `mapstructure:"kernel_options"` + KernelOptionsPost string `mapstructure:"kernel_options_post"` + Initrd string `mapstructure:"initrd"` + MGMTClasses []string `mapstructure:"mgmt_classes"` + Name string `mapstructure:"name"` + OSVersion string `mapstructure:"os_version"` + Owners []string `mapstructure:"owners"` + RedHatManagementKey string `mapstructure:"redhat_management_key"` + RedHatManagementServer string `mapstructure:"redhat_management_server"` + TemplateFiles string `mapstructure:"template_files"` + + //KSMeta string `mapstructure:"ks_meta"` + //SourceRepos []string `mapstructure:"source_repos"` +} + +// GetDistros returns all systems in Cobbler. +func (c *Client) GetDistros() ([]*Distro, error) { + var distros []*Distro + + result, err := c.Call("get_distros", "-1", c.Token) + if err != nil { + return nil, err + } + + for _, d := range result.([]interface{}) { + var distro Distro + decodedResult, err := decodeCobblerItem(d, &distro) + if err != nil { + return nil, err + } + + distros = append(distros, decodedResult.(*Distro)) + } + + return distros, nil +} + +// GetDistro returns a single distro obtained by its name. +func (c *Client) GetDistro(name string) (*Distro, error) { + var distro Distro + + result, err := c.Call("get_distro", name, c.Token) + if result == "~" { + return nil, fmt.Errorf("Distro %s not found.", name) + } + + if err != nil { + return nil, err + } + + decodeResult, err := decodeCobblerItem(result, &distro) + if err != nil { + return nil, err + } + + return decodeResult.(*Distro), nil +} + +// CreateDistro creates a distro. +func (c *Client) CreateDistro(distro Distro) (*Distro, error) { + // Make sure a distro with the same name does not already exist + if _, err := c.GetDistro(distro.Name); err == nil { + return nil, fmt.Errorf("A Distro with the name %s already exists.", distro.Name) + } + + result, err := c.Call("new_distro", c.Token) + if err != nil { + return nil, err + } + newId := result.(string) + + item := reflect.ValueOf(&distro).Elem() + if err := c.updateCobblerFields("distro", item, newId); err != nil { + return nil, err + } + + if _, err := c.Call("save_distro", newId, c.Token); err != nil { + return nil, err + } + + return c.GetDistro(distro.Name) +} + +// UpdateDistro updates a single distro. +func (c *Client) UpdateDistro(distro *Distro) error { + item := reflect.ValueOf(distro).Elem() + id, err := c.GetItemHandle("distro", distro.Name) + if err != nil { + return err + } + + if err := c.updateCobblerFields("distro", item, id); err != nil { + return err + } + + if _, err := c.Call("save_distro", id, c.Token); err != nil { + return err + } + + return nil +} + +// DeleteDistro deletes a single distro by its name. +func (c *Client) DeleteDistro(name string) error { + _, err := c.Call("remove_distro", name, c.Token) + return err +} diff --git a/vendor/github.com/jtopjian/cobblerclient/kickstart_file.go b/vendor/github.com/jtopjian/cobblerclient/kickstart_file.go new file mode 100644 index 000000000000..368777b82a9f --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/kickstart_file.go @@ -0,0 +1,56 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +type KickstartFile struct { + Name string // The name the kickstart file will be saved in Cobbler + Body string // The contents of the kickstart file +} + +// Creates a kickstart file in Cobbler. +// Takes a KickstartFile struct as input. +// Returns true/false and error if creation failed. +func (c *Client) CreateKickstartFile(f KickstartFile) error { + _, err := c.Call("read_or_write_kickstart_template", f.Name, false, f.Body, c.Token) + return err +} + +// Gets a kickstart file in Cobbler. +// Takes a kickstart file name as input. +// Returns *KickstartFile and error if read failed. +func (c *Client) GetKickstartFile(ksName string) (*KickstartFile, error) { + result, err := c.Call("read_or_write_kickstart_template", ksName, true, "", c.Token) + + if err != nil { + return nil, err + } + + ks := KickstartFile{ + Name: ksName, + Body: result.(string), + } + + return &ks, nil +} + +// Deletes a kickstart file in Cobbler. +// Takes a kickstart file name as input. +// Returns error if delete failed. +func (c *Client) DeleteKickstartFile(name string) error { + _, err := c.Call("read_or_write_kickstart_template", name, false, -1, c.Token) + return err +} diff --git a/vendor/github.com/jtopjian/cobblerclient/methods.txt b/vendor/github.com/jtopjian/cobblerclient/methods.txt new file mode 100644 index 000000000000..8d3dcac27d11 --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/methods.txt @@ -0,0 +1,1990 @@ +This is a short document that lists the xmlrpc calls needed. + +-- login: +curl -XPOST -d ' + + login + + + + cobbler + + + + + cobbler + + + + +' http://localhost:25151/ + + + + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + + +-- create new system: +curl -XPOST -d ' + + new_system + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + +' http://localhost:25151/ + + + + + + + ___NEW___system::qxK4MZaxtZzTaxZW98nNZWbgkmyTXtU14Q== + + + + + +-- set system name: +curl -XPOST -d ' + + modify_system + + + + ___NEW___system::qxK4MZaxtZzTaxZW98nNZWbgkmyTXtU14Q== + + + + + name + + + + + systemname01 + + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + +' http://localhost:25151/ + + + + + + 1 + + + + +-- set system profile: +curl -XPOST -d ' + + modify_system + + + + ___NEW___system::qxK4MZaxtZzTaxZW98nNZWbgkmyTXtU14Q== + + + + + profile + + + + + centos7-x86_64 + + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + +' http://localhost:25151/ + +-- configure network interface (still need to figure out name of gateway property): +curl -XPOST -d ' + + modify_system + + + + ___NEW___system::ridhgThzSpL5wwjdWSonGsM8nv/HtSfNQQ== + + + + + modify_interface + + + + + + + macaddress-eth0 + + 01:02:03:04:05:06 + + + + ipaddress-eth0 + + 10.20.30.40 + + + + dnsname-eth0 + + systemname01.domain.tld + + + + subnetmask-eth0 + + 255.255.255.0 + + + + if-gateway-eth0 + + 10.20.30.1 + + + + + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + +' http://localhost:25151/ + + + + + + + 1 + + + + + +-- save the system: +curl -XPOST -d ' + + save_system + + + + ___NEW___system::qxK4MZaxtZzTaxZW98nNZWbgkmyTXtU14Q== + + + + + ZyWe2dxicTWGsDpbo+WT3z1WZ2trEgfoaw== + + + + +' http://localhost:25151/ + + + + + + 1 + + + + +result will be 0 if save failed. + + +--- sync +curl -XPOST -d ' + + sync + + + zYli1fFyS3Hi6qlSPMorEWfiUhBfAuOsrA== + + + +' http://localhost:25151/ + + + + + + 1 + + + + +-- create a kickstart file: +curl -XPOST -d ' + + read_or_write_kickstart_template + + + + /var/lib/cobbler/kickstarts/foo.ks + + + + + 0 + + + + + # test content for the kickstart file + + + + + zYli1fFyS3Hi6qlSPMorEWfiUhBfAuOsrA== + + + +' http://localhost:25151/ + +-- get a kickstart file: +curl -XPOST -d ' + + read_or_write_kickstart_template + + + + /var/lib/cobbler/kickstarts/foo.ks + + + + + 1 + + + + + + + + + + securetoken99 + + + +' http://localhost:25151/ + +-- create a snippet: +curl -XPOST -d ' + + read_or_write_snippet + + + + /var/lib/cobbler/snippets/foo + + + + + 0 + + + + + # test content for the snippet file + + + + + zYli1fFyS3Hi6qlSPMorEWfiUhBfAuOsrA== + + + +' http://localhost:25151/ + +-- get a snippet: +curl -XPOST -d ' + + read_or_write_snippet + + + + /var/lib/cobbler/snippets/some-snippet + + + + + 1 + + + + + + + + + + securetoken99 + + + +' http://localhost:25151/ + +-- get distros: +curl -XPOST -d ' + get_distros + + + + -1 + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + + + + comment + + + + + + kernel + + /var/www/cobbler/ks_mirror/Ubuntu-14.04/install/netboot/ubuntu-installer/amd64/linux + + + + uid + + MTQ1MTg1NjMzNC4yMTk0MTg3My43Mzg0NTM + + + + kernel_options_post + + + + + + + redhat_management_key + + <<inherit>> + + + + kernel_options + + + + + + + redhat_management_server + + <<inherit>> + + + + initrd + + /var/www/cobbler/ks_mirror/Ubuntu-14.04/install/netboot/ubuntu-installer/amd64/initrd.gz + + + + mtime + + 1451856336.460383 + + + + template_files + + + + + + + ks_meta + + + + tree + + http://@@http_server@@/cblr/links/Ubuntu-14.04-x86_64 + + + + + + + boot_files + + + + + + + breed + + ubuntu + + + + os_version + + trusty + + + + mgmt_classes + + + + + + + + + fetchable_files + + + + + + + tree_build_time + + 0 + + + + arch + + x86_64 + + + + name + + Ubuntu-14.04-x86_64 + + + + owners + + + + + admin + + + + + + + ctime + + 1451856334.214615 + + + + source_repos + + + + + + + + + depth + + 0 + + + + + + + + + + + +-- get distro: +curl -XPOST -d ' + get_distro + + + + Ubuntu-14.04-x86_64 + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + comment + + + + + + kernel + + /var/www/cobbler/ks_mirror/Ubuntu-14.04/install/netboot/ubuntu-installer/amd64/linux + + + + uid + + MTQ1MTg1NjMzNC4yMTk0MTg3My43Mzg0NTM + + + + kernel_options_post + + + + + + redhat_management_key + + <<inherit>> + + + + kernel_options + + + + + + redhat_management_server + + <<inherit>> + + + + initrd + + /var/www/cobbler/ks_mirror/Ubuntu-14.04/install/netboot/ubuntu-installer/amd64/initrd.gz + + + + mtime + + 1451856336.460383 + + + + template_files + + + + + + ks_meta + + tree=http://@@http_server@@/cblr/links/Ubuntu-14.04-x86_64 + + + + boot_files + + + + + + breed + + ubuntu + + + + os_version + + trusty + + + + mgmt_classes + + + + + + + + fetchable_files + + + + + + tree_build_time + + 0 + + + + arch + + x86_64 + + + + name + + Ubuntu-14.04-x86_64 + + + + owners + + + + + admin + + + + + + + ctime + + 1451856334.214615 + + + + source_repos + + + + + + + + depth + + 0 + + + + + + + + +-- get profiles: +curl -XPOST -d ' + get_profiles + + + + -1 + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + + + + + comment + + + + + + kickstart + + /var/lib/cobbler/kickstarts/sample.seed + + + + name_servers_search + + + + + + + + + ks_meta + + + + + + + kernel_options_post + + + + + + + repos + + + + + + + + + redhat_management_key + + <<inherit>> + + + + virt_path + + + + + + kernel_options + + + + + + + virt_file_size + + 5 + + + + mtime + + 1451856335.087784 + + + + enable_gpxe + + 0 + + + + template_files + + + + + + + uid + + MTQ1MTg1NjMzNS4wOTk4MTczMTYuMTI1ODc + + + + virt_auto_boot + + 1 + + + + virt_cpus + + 1 + + + + mgmt_parameters + + <<inherit>> + + + + boot_files + + + + + + + mgmt_classes + + + + + + + + + distro + + Ubuntu-14.04-x86_64 + + + + virt_disk_driver + + raw + + + + virt_bridge + + virbr0 + + + + parent + + + + + + virt_type + + kvm + + + + proxy + + + + + + enable_menu + + 1 + + + + fetchable_files + + + + + + + name_servers + + + + + + + + + name + + Ubuntu-14.04-x86_64 + + + + dhcp_tag + + default + + + + owners + + + + + admin + + + + + + + ctime + + 1451856335.087784 + + + + virt_ram + + 512 + + + + server + + <<inherit>> + + + + redhat_management_server + + <<inherit>> + + + + depth + + 1 + + + + template_remote_kickstarts + + 0 + + + + + + + + + + + +-- get profile: +curl -XPOST -d ' + get_profile + + + + Ubuntu-14.04-x86_64 + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + + comment + + + + + + kickstart + + /var/lib/cobbler/kickstarts/sample.seed + + + + name_servers_search + + + + + + + + + ks_meta + + + + + + kernel_options_post + + + + + + repos + + + + + + redhat_management_key + + <<inherit>> + + + + virt_path + + + + + + kernel_options + + + + + + virt_file_size + + 5 + + + + mtime + + 1451856335.087784 + + + + enable_gpxe + + 0 + + + + template_files + + + + + + uid + + MTQ1MTg1NjMzNS4wOTk4MTczMTYuMTI1ODc + + + + virt_auto_boot + + 1 + + + + virt_cpus + + 1 + + + + mgmt_parameters + + <<inherit>> + + + + boot_files + + + + + + mgmt_classes + + + + + + + + + distro + + Ubuntu-14.04-x86_64 + + + + virt_disk_driver + + raw + + + + virt_bridge + + virbr0 + + + + parent + + + + + + virt_type + + kvm + + + + proxy + + + + + + enable_menu + + 1 + + + + fetchable_files + + + + + + name_servers + + + + + + + + + name + + Ubuntu-14.04-x86_64 + + + + dhcp_tag + + default + + + + owners + + + + + admin + + + + + + + ctime + + 1451856335.087784 + + + + virt_ram + + 512 + + + + server + + <<inherit>> + + + + redhat_management_server + + <<inherit>> + + + + depth + + 1 + + + + template_remote_kickstarts + + 0 + + + + + + + + +-- get systems: +curl -XPOST -d ' + get_systems + + + + -1 + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + + + + + comment + + + + + + profile + + Ubuntu-14.04-x86_64 + + + + kickstart + + <<inherit>> + + + + name_servers_search + + + + + + + + + ks_meta + + + + + + + kernel_options_post + + + + + + + image + + + + + + redhat_management_key + + <<inherit>> + + + + power_type + + ether_wake + + + + power_user + + + + + + kernel_options + + + + + + + virt_file_size + + <<inherit>> + + + + mtime + + 1451856819.487791 + + + + enable_gpxe + + 0 + + + + template_files + + + + + + + gateway + + + + + + uid + + MTQ1MTg1NjgxOS40OTE4ODYyODQuNzAxMTY + + + + virt_auto_boot + + <<inherit>> + + + + monit_enabled + + 0 + + + + virt_cpus + + <<inherit>> + + + + mgmt_parameters + + <<inherit>> + + + + boot_files + + + + + + + hostname + + + + + + repos_enabled + + 0 + + + + name + + test + + + + virt_type + + <<inherit>> + + + + mgmt_classes + + + + + + + + + power_pass + + + + + + netboot_enabled + + 1 + + + + ipv6_autoconfiguration + + 0 + + + + status + + production + + + + virt_path + + <<inherit>> + + + + interfaces + + + + + + + power_address + + + + + + proxy + + <<inherit>> + + + + fetchable_files + + + + + + + name_servers + + + + + + + + + ldap_enabled + + 0 + + + + ipv6_default_device + + + + + + virt_pxe_boot + + 0 + + + + virt_disk_driver + + <<inherit>> + + + + owners + + + + + admin + + + + + + + ctime + + 1451856819.487791 + + + + virt_ram + + <<inherit>> + + + + power_id + + + + + + server + + <<inherit>> + + + + redhat_management_server + + <<inherit>> + + + + depth + + 2 + + + + ldap_type + + authconfig + + + + template_remote_kickstarts + + 0 + + + + + + + + + + + +-- get system: +curl -XPOST -d ' + get_system + + + + test + + + + + 4f8464lmE6s+6YmQcOr+ACJvdyd5kIzV0w== + + + + +' http://localhost:25151/ + + + + + + + + + comment + + + + + + profile + + Ubuntu-14.04-x86_64 + + + + kickstart + + <<inherit>> + + + + name_servers_search + + + + + + + + + ks_meta + + + + + + kernel_options_post + + + + + + image + + + + + + redhat_management_key + + <<inherit>> + + + + power_type + + ether_wake + + + + power_user + + + + + + kernel_options + + + + + + virt_file_size + + <<inherit>> + + + + mtime + + 1451856819.487791 + + + + enable_gpxe + + 0 + + + + template_files + + + + + + gateway + + + + + + uid + + MTQ1MTg1NjgxOS40OTE4ODYyODQuNzAxMTY + + + + virt_auto_boot + + <<inherit>> + + + + monit_enabled + + 0 + + + + virt_cpus + + <<inherit>> + + + + mgmt_parameters + + <<inherit>> + + + + boot_files + + + + + + hostname + + + + + + repos_enabled + + 0 + + + + name + + test + + + + virt_type + + <<inherit>> + + + + mgmt_classes + + + + + + + + + power_pass + + + + + + netboot_enabled + + 1 + + + + ipv6_autoconfiguration + + 0 + + + + status + + production + + + + virt_path + + <<inherit>> + + + + interfaces + + + + + + + power_address + + + + + + proxy + + <<inherit>> + + + + fetchable_files + + + + + + name_servers + + + + + + + + + ldap_enabled + + 0 + + + + ipv6_default_device + + + + + + virt_pxe_boot + + 0 + + + + virt_disk_driver + + <<inherit>> + + + + owners + + + + + admin + + + + + + + ctime + + 1451856819.487791 + + + + virt_ram + + <<inherit>> + + + + power_id + + + + + + server + + <<inherit>> + + + + redhat_management_server + + <<inherit>> + + + + depth + + 2 + + + + ldap_type + + authconfig + + + + template_remote_kickstarts + + 0 + + + + + + + diff --git a/vendor/github.com/jtopjian/cobblerclient/profile.go b/vendor/github.com/jtopjian/cobblerclient/profile.go new file mode 100644 index 000000000000..5aa971e5207d --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/profile.go @@ -0,0 +1,230 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +import ( + "fmt" + "reflect" +) + +// Profile is a created profile. +type Profile struct { + // These are internal fields and cannot be modified. + Ctime float64 `mapstructure:"ctime" cobbler:"noupdate"` // TODO: convert to time + Depth int `mapstructure:"depth" cobbler:"noupdate"` + ID string `mapstructure:"uid" cobbler:"noupdate"` + Mtime float64 `mapstructure:"mtime" cobbler:"noupdate"` // TODO: convert to time + ReposEnabled bool `mapstructure:"repos_enabled" cobbler:"noupdate"` + + BootFiles string `mapstructure:"boot_files"` + Comment string `mapstructure:"comment"` + Distro string `mapstructure:"distro"` + EnableGPXE bool `mapstructure:"enable_gpxe"` + EnableMenu bool `mapstructure:"enable_menu"` + FetchableFiles string `mapstructure:"fetchable_files"` + KernelOptions string `mapstructure:"kernel_options"` + KernelOptionsPost string `mapstructure:"kernel_options_post"` + Kickstart string `mapstructure:"kickstart"` + KSMeta string `mapstructure:"ks_meta"` + MGMTClasses []string `mapstructure:"mgmt_classes"` + MGMTParameters string `mapstructure:"mgmt_parameters"` + Name string `mapstructure:"name"` + NameServersSearch []string `mapstructure:"name_servers_search"` + NameServers []string `mapstructure:"name_servers"` + Owners []string `mapstructure:"owners"` + Parent string `mapstructure:"parent"` + Proxy string `mapstructure:"proxy"` + RedHatManagementKey string `mapstructure:"redhat_management_key"` + RedHatManagementServer string `mapstructure:"redhat_management_server"` + Repos []string `mapstructure:"repos"` + Server string `mapstructure:"server"` + TemplateFiles string `mapstructure:"template_files"` + TemplateRemoteKickstarts int `mapstructure:"template_remote_kickstarts"` + VirtAutoBoot string `mapstructure:"virt_auto_boot"` + VirtBridge string `mapstructure:"virt_bridge"` + VirtCPUs string `mapstructure:"virt_cpus"` + VirtDiskDriver string `mapstructure:"virt_disk_driver"` + VirtFileSize string `mapstructure:"virt_file_size"` + VirtPath string `mapstructure:"virt_path"` + VirtRam string `mapstructure:"virt_ram"` + VirtType string `mapstructure:"virt_type"` + + Client +} + +// GetProfiles returns all systems in Cobbler. +func (c *Client) GetProfiles() ([]*Profile, error) { + var profiles []*Profile + + result, err := c.Call("get_profiles", "-1", c.Token) + if err != nil { + return nil, err + } + + for _, p := range result.([]interface{}) { + var profile Profile + decodedResult, err := decodeCobblerItem(p, &profile) + if err != nil { + return nil, err + } + decodedProfile := decodedResult.(*Profile) + decodedProfile.Client = *c + profiles = append(profiles, decodedProfile) + } + + return profiles, nil +} + +// GetProfile returns a single profile obtained by its name. +func (c *Client) GetProfile(name string) (*Profile, error) { + var profile Profile + + result, err := c.Call("get_profile", name, c.Token) + if err != nil { + return &profile, err + } + + if result == "~" { + return nil, fmt.Errorf("Profile %s not found.", name) + } + + decodeResult, err := decodeCobblerItem(result, &profile) + if err != nil { + return nil, err + } + + s := decodeResult.(*Profile) + s.Client = *c + + return s, nil +} + +// CreateProfile creates a system. +// It ensures that a Distro is set and then sets other default values. +func (c *Client) CreateProfile(profile Profile) (*Profile, error) { + // Check if a profile with the same name already exists + if _, err := c.GetProfile(profile.Name); err == nil { + return nil, fmt.Errorf("A profile with the name %s already exists.", profile.Name) + } + + if profile.Distro == "" { + return nil, fmt.Errorf("A profile must have a distro set.") + } + + /* + // Set default values. I guess these aren't taken care of by Cobbler? + if system.BootFiles == "" { + system.BootFiles = "<>" + } + + if system.FetchableFiles == "" { + system.FetchableFiles = "<>" + } + + */ + + if profile.MGMTParameters == "" { + profile.MGMTParameters = "<>" + } + + if profile.VirtAutoBoot == "" { + profile.VirtAutoBoot = "0" + } + + if profile.VirtRam == "" { + profile.VirtRam = "<>" + } + + if profile.VirtType == "" { + profile.VirtType = "<>" + } + + /* + + if system.PowerType == "" { + system.PowerType = "ipmilan" + } + + if system.Status == "" { + system.Status = "production" + } + + if system.VirtCPUs == "" { + system.VirtCPUs = "<>" + } + + if system.VirtDiskDriver == "" { + system.VirtDiskDriver = "<>" + } + + if system.VirtFileSize == "" { + system.VirtFileSize = "<>" + } + + if system.VirtPath == "" { + system.VirtPath = "<>" + } + + */ + + // To create a profile via the Cobbler API, first call new_profile to obtain an ID + result, err := c.Call("new_profile", c.Token) + if err != nil { + return nil, err + } + newId := result.(string) + + // Set the value of all fields + item := reflect.ValueOf(&profile).Elem() + if err := c.updateCobblerFields("profile", item, newId); err != nil { + return nil, err + } + + // Save the final profile + if _, err := c.Call("save_profile", newId, c.Token); err != nil { + return nil, err + } + + // Return a clean copy of the profile + return c.GetProfile(profile.Name) +} + +// UpdateProfile updates a single profile. +func (c *Client) UpdateProfile(profile *Profile) error { + item := reflect.ValueOf(profile).Elem() + id, err := c.GetItemHandle("profile", profile.Name) + if err != nil { + return err + } + + if err := c.updateCobblerFields("profile", item, id); err != nil { + return err + } + + // Save the final profile + if _, err := c.Call("save_profile", id, c.Token); err != nil { + return err + } + + return nil +} + +// DeleteProfile deletes a single profile by its name. +func (c *Client) DeleteProfile(name string) error { + _, err := c.Call("remove_profile", name, c.Token) + return err +} diff --git a/vendor/github.com/jtopjian/cobblerclient/snippet.go b/vendor/github.com/jtopjian/cobblerclient/snippet.go new file mode 100644 index 000000000000..69cf8afc3455 --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/snippet.go @@ -0,0 +1,56 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +type Snippet struct { + Name string // The name the snippet file will be saved in Cobbler + Body string // The contents of the kickstart file +} + +// Creates a snippet in Cobbler. +// Takes a Snippet struct as input +// Returns true/false and error if creation failed. +func (c *Client) CreateSnippet(s Snippet) error { + _, err := c.Call("read_or_write_snippet", s.Name, false, s.Body, c.Token) + return err +} + +// Gets a snippet file in Cobbler. +// Takes a snippet file name as input. +// Returns *Snippet and error if read failed. +func (c *Client) GetSnippet(name string) (*Snippet, error) { + result, err := c.Call("read_or_write_snippet", name, true, "", c.Token) + + if err != nil { + return nil, err + } + + snippet := Snippet{ + Name: name, + Body: result.(string), + } + + return &snippet, nil +} + +// Gets a snippet file in Cobbler. +// Takes a snippet file name as input. +// Returns error if delete failed. +func (c *Client) DeleteSnippet(name string) error { + _, err := c.Call("read_or_write_snippet", name, false, -1, c.Token) + return err +} diff --git a/vendor/github.com/jtopjian/cobblerclient/system.go b/vendor/github.com/jtopjian/cobblerclient/system.go new file mode 100644 index 000000000000..1f93d8428f19 --- /dev/null +++ b/vendor/github.com/jtopjian/cobblerclient/system.go @@ -0,0 +1,336 @@ +/* +Copyright 2015 Container Solutions + +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 cobblerclient + +import ( + "fmt" + "reflect" + + "github.com/fatih/structs" + "github.com/mitchellh/mapstructure" +) + +// System is a created system. +type System struct { + // These are internal fields and cannot be modified. + Ctime float64 `mapstructure:"ctime" cobbler:"noupdate"` // TODO: convert to time + Depth int `mapstructure:"depth" cobbler:"noupdate"` + ID string `mapstructure:"uid" cobbler:"noupdate"` + IPv6Autoconfiguration bool `mapstructure:"ipv6_autoconfiguration" cobbler:"noupdate"` + Mtime float64 `mapstructure:"mtime" cobbler:"noupdate"` // TODO: convert to time + ReposEnabled bool `mapstructure:"repos_enabled" cobbler:"noupdate"` + + BootFiles string `mapstructure:"boot_files"` + Comment string `mapstructure:"comment"` + EnableGPXE bool `mapstructure:"enable_gpxe"` + FetchableFiles string `mapstructure:"fetchable_files"` + Gateway string `mapstructure:"gateway"` + Hostname string `mapstructure:"hostname"` + Image string `mapstructure:"image"` + Interfaces map[string]interface{} `mapstructure:"interfaces" cobbler:"noupdate"` + IPv6DefaultDevice string `mapstructure:"ipv6_default_device"` + KernelOptions string `mapstructure:"kernel_options"` + KernelOptionsPost string `mapstructure:"kernel_options_post"` + Kickstart string `mapstructure:"kickstart"` + KSMeta string `mapstructure:"ks_meta"` + LDAPEnabled bool `mapstructure:"ldap_enabled"` + LDAPType string `mapstructure:"ldap_type"` + MGMTClasses []string `mapstructure:"mgmt_classes"` + MGMTParameters string `mapstructure:"mgmt_parameters"` + MonitEnabled bool `mapstructure:"monit_enabled"` + Name string `mapstructure:"name"` + NameServersSearch []string `mapstructure:"name_servers_search"` + NameServers []string `mapstructure:"name_servers"` + NetbootEnabled bool `mapstructure:"netboot_enabled"` + Owners []string `mapstructure:"owners"` + PowerAddress string `mapstructure:"power_address"` + PowerID string `mapstructure:"power_id"` + PowerPass string `mapstructure:"power_pass"` + PowerType string `mapstructure:"power_type"` + PowerUser string `mapstructure:"power_user"` + Profile string `mapstructure:"profile"` + Proxy string `mapstructure:"proxy"` + RedHatManagementKey string `mapstructure:"redhat_management_key"` + RedHatManagementServer string `mapstructure:"redhat_management_server"` + Status string `mapstructure:"status"` + TemplateFiles string `mapstructure:"template_files"` + TemplateRemoteKickstarts int `mapstructure:"template_remote_kickstarts"` + VirtAutoBoot string `mapstructure:"virt_auto_boot"` + VirtCPUs string `mapstructure:"virt_cpus"` + VirtDiskDriver string `mapstructure:"virt_disk_driver"` + VirtFileSize string `mapstructure:"virt_file_size"` + VirtPath string `mapstructure:"virt_path"` + VirtPXEBoot int `mapstructure:"virt_pxe_boot"` + VirtRam string `mapstructure:"virt_ram"` + VirtType string `mapstructure:"virt_type"` + + Client +} + +// Interface is an interface in a system. +type Interface struct { + CNAMEs []string `mapstructure:"cnames" structs:"cnames"` + DHCPTag string `mapstructure:"dhcp_tag" structs:"dhcp_tag"` + DNSName string `mapstructure:"dns_name" structs:"dns_name"` + BondingOpts string `mapstructure:"bonding_opts" structs:"bonding_opts"` + BridgeOpts string `mapstructure:"bridge_opts" structs:"bridge_opts"` + Gateway string `mapstructure:"if_gateway" structs:"if_gateway"` + InterfaceType string `mapstructure:"interface_type" structs:"interface_type"` + InterfaceMaster string `mapstructure:"interface_master" structs:"interface_master"` + IPAddress string `mapstructure:"ip_address" structs:"ip_address"` + IPv6Address string `mapstructure:"ipv6_address" structs:"ipv6_address"` + IPv6Secondaries []string `mapstructure:"ipv6_secondaries" structs:"ipv6_secondaries"` + IPv6MTU string `mapstructure:"ipv6_mtu" structs:"ipv6_mtu"` + IPv6StaticRoutes []string `mapstructure:"ipv6_static_routes" structs:"ipv6_static_routes"` + IPv6DefaultGateway string `mapstructure:"ipv6_default_gateway structs:"ipv6_default_gateway"` + MACAddress string `mapstructure:"mac_address" structs:"mac_address"` + Management bool `mapstructure:"management" structs:"managment"` + Netmask string `mapstructure:"netmask" structs:"netmask"` + Static bool `mapstructure:"static" structs:"static"` + StaticRoutes []string `mapstructure:"static_routes" structs:"static_routes"` + VirtBridge string `mapstructure:"virt_bridge" structs:"virt_bridge"` +} + +// Interfaces is a collection of interfaces in a system. +type Interfaces map[string]Interface + +// GetSystems returns all systems in Cobbler. +func (c *Client) GetSystems() ([]*System, error) { + var systems []*System + + result, err := c.Call("get_systems", "", c.Token) + if err != nil { + return nil, err + } + + for _, s := range result.([]interface{}) { + var system System + decodedResult, err := decodeCobblerItem(s, &system) + if err != nil { + return nil, err + } + decodedSystem := decodedResult.(*System) + decodedSystem.Client = *c + systems = append(systems, decodedSystem) + } + + return systems, nil +} + +// GetSystem returns a single system obtained by its name. +func (c *Client) GetSystem(name string) (*System, error) { + var system System + + result, err := c.Call("get_system", name, c.Token) + if err != nil { + return &system, err + } + + if result == "~" { + return nil, fmt.Errorf("System %s not found.", name) + } + + decodeResult, err := decodeCobblerItem(result, &system) + if err != nil { + return nil, err + } + + s := decodeResult.(*System) + s.Client = *c + + return s, nil +} + +// CreateSystem creates a system. +// It ensures that either a Profile or Image are set and then sets other default values. +func (c *Client) CreateSystem(system System) (*System, error) { + // Check if a system with the same name already exists + if _, err := c.GetSystem(system.Name); err == nil { + return nil, fmt.Errorf("A system with the name %s already exists.", system.Name) + } + + if system.Profile == "" && system.Image == "" { + return nil, fmt.Errorf("A system must have a profile or image set.") + } + + // Set default values. I guess these aren't taken care of by Cobbler? + if system.BootFiles == "" { + system.BootFiles = "<>" + } + + if system.FetchableFiles == "" { + system.FetchableFiles = "<>" + } + + if system.MGMTParameters == "" { + system.MGMTParameters = "<>" + } + + if system.PowerType == "" { + system.PowerType = "ipmilan" + } + + if system.Status == "" { + system.Status = "production" + } + + if system.VirtAutoBoot == "" { + system.VirtAutoBoot = "0" + } + + if system.VirtCPUs == "" { + system.VirtCPUs = "<>" + } + + if system.VirtDiskDriver == "" { + system.VirtDiskDriver = "<>" + } + + if system.VirtFileSize == "" { + system.VirtFileSize = "<>" + } + + if system.VirtPath == "" { + system.VirtPath = "<>" + } + + if system.VirtRam == "" { + system.VirtRam = "<>" + } + + if system.VirtType == "" { + system.VirtType = "<>" + } + + // To create a system via the Cobbler API, first call new_system to obtain an ID + result, err := c.Call("new_system", c.Token) + if err != nil { + return nil, err + } + newId := result.(string) + + // Set the value of all fields + item := reflect.ValueOf(&system).Elem() + if err := c.updateCobblerFields("system", item, newId); err != nil { + return nil, err + } + + // Save the final system + if _, err := c.Call("save_system", newId, c.Token); err != nil { + return nil, err + } + + // Return a clean copy of the system + return c.GetSystem(system.Name) +} + +// UpdateSystem updates a single system. +func (c *Client) UpdateSystem(system *System) error { + item := reflect.ValueOf(system).Elem() + id, err := c.GetItemHandle("system", system.Name) + if err != nil { + return err + } + return c.updateCobblerFields("system", item, id) +} + +// DeleteSystem deletes a single system by its name. +func (c *Client) DeleteSystem(name string) error { + _, err := c.Call("remove_system", name, c.Token) + return err +} + +func (s *System) CreateInterface(name string, iface Interface) error { + i := structs.Map(iface) + nic := make(map[string]interface{}) + for key, value := range i { + attrName := fmt.Sprintf("%s-%s", key, name) + nic[attrName] = value + } + + systemId, err := s.Client.GetItemHandle("system", s.Name) + if err != nil { + return err + } + + if _, err := s.Client.Call("modify_system", systemId, "modify_interface", nic, s.Client.Token); err != nil { + return err + } + + // Save the final system + if _, err := s.Client.Call("save_system", systemId, s.Client.Token); err != nil { + return err + } + + return nil +} + +// GetInterfaces returns all interfaces in a System. +func (s *System) GetInterfaces() (Interfaces, error) { + nics := make(Interfaces) + for nicName, nicData := range s.Interfaces { + var nic Interface + if err := mapstructure.Decode(nicData, &nic); err != nil { + return nil, err + } + nics[nicName] = nic + } + + return nics, nil +} + +// GetInterface returns a single interface in a System. +func (s *System) GetInterface(name string) (Interface, error) { + nics := make(Interfaces) + var iface Interface + for nicName, nicData := range s.Interfaces { + var nic Interface + if err := mapstructure.Decode(nicData, &nic); err != nil { + return iface, err + } + nics[nicName] = nic + } + + if iface, ok := nics[name]; ok { + return iface, nil + } else { + return iface, fmt.Errorf("Interface %s not found.", name) + } +} + +// DeleteInterface deletes a single interface in a System. +func (s *System) DeleteInterface(name string) error { + if _, err := s.GetInterface(name); err != nil { + return err + } + + systemId, err := s.Client.GetItemHandle("system", s.Name) + if err != nil { + return err + } + + if _, err := s.Client.Call("modify_system", systemId, "delete_interface", name, s.Client.Token); err != nil { + return err + } + + // Save the final system + if _, err := s.Client.Call("save_system", systemId, s.Client.Token); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/kolo/xmlrpc/LICENSE b/vendor/github.com/kolo/xmlrpc/LICENSE new file mode 100644 index 000000000000..8103dd139136 --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 Dmitry Maksimov + +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. diff --git a/vendor/github.com/kolo/xmlrpc/README.md b/vendor/github.com/kolo/xmlrpc/README.md new file mode 100644 index 000000000000..12b7692e9077 --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/README.md @@ -0,0 +1,79 @@ +## Overview + +xmlrpc is an implementation of client side part of XMLRPC protocol in Go language. + +## Installation + +To install xmlrpc package run `go get github.com/kolo/xmlrpc`. To use +it in application add `"github.com/kolo/xmlrpc"` string to `import` +statement. + +## Usage + + client, _ := xmlrpc.NewClient("https://bugzilla.mozilla.org/xmlrpc.cgi", nil) + result := struct{ + Version string `xmlrpc:"version"` + }{} + client.Call("Bugzilla.version", nil, &result) + fmt.Printf("Version: %s\n", result.Version) // Version: 4.2.7+ + +Second argument of NewClient function is an object that implements +[http.RoundTripper](http://golang.org/pkg/net/http/#RoundTripper) +interface, it can be used to get more control over connection options. +By default it initialized by http.DefaultTransport object. + +### Arguments encoding + +xmlrpc package supports encoding of native Go data types to method +arguments. + +Data types encoding rules: +* int, int8, int16, int32, int64 encoded to int; +* float32, float64 encoded to double; +* bool encoded to boolean; +* string encoded to string; +* time.Time encoded to datetime.iso8601; +* xmlrpc.Base64 encoded to base64; +* slice decoded to array; + +Structs decoded to struct by following rules: +* all public field become struct members; +* field name become member name; +* if field has xmlrpc tag, its value become member name. + +Server method can accept few arguments, to handle this case there is +special approach to handle slice of empty interfaces (`[]interface{}`). +Each value of such slice encoded as separate argument. + +### Result decoding + +Result of remote function is decoded to native Go data type. + +Data types decoding rules: +* int, i4 decoded to int, int8, int16, int32, int64; +* double decoded to float32, float64; +* boolean decoded to bool; +* string decoded to string; +* array decoded to slice; +* structs decoded following the rules described in previous section; +* datetime.iso8601 decoded as time.Time data type; +* base64 decoded to string. + +## Implementation details + +xmlrpc package contains clientCodec type, that implements [rpc.ClientCodec](http://golang.org/pkg/net/rpc/#ClientCodec) +interface of [net/rpc](http://golang.org/pkg/net/rpc) package. + +xmlrpc package works over HTTP protocol, but some internal functions +and data type were made public to make it easier to create another +implementation of xmlrpc that works over another protocol. To encode +request body there is EncodeMethodCall function. To decode server +response Response data type can be used. + +## Contribution + +Feel free to fork the project, submit pull requests, ask questions. + +## Authors + +Dmitry Maksimov (dmtmax@gmail.com) diff --git a/vendor/github.com/kolo/xmlrpc/client.go b/vendor/github.com/kolo/xmlrpc/client.go new file mode 100644 index 000000000000..fb66b65fbc8c --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/client.go @@ -0,0 +1,144 @@ +package xmlrpc + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/http/cookiejar" + "net/rpc" + "net/url" +) + +type Client struct { + *rpc.Client +} + +// clientCodec is rpc.ClientCodec interface implementation. +type clientCodec struct { + // url presents url of xmlrpc service + url *url.URL + + // httpClient works with HTTP protocol + httpClient *http.Client + + // cookies stores cookies received on last request + cookies http.CookieJar + + // responses presents map of active requests. It is required to return request id, that + // rpc.Client can mark them as done. + responses map[uint64]*http.Response + + response *Response + + // ready presents channel, that is used to link request and it`s response. + ready chan uint64 +} + +func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) { + httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args) + + if codec.cookies != nil { + for _, cookie := range codec.cookies.Cookies(codec.url) { + httpRequest.AddCookie(cookie) + } + } + + if err != nil { + return err + } + + var httpResponse *http.Response + httpResponse, err = codec.httpClient.Do(httpRequest) + + if err != nil { + return err + } + + if codec.cookies != nil { + codec.cookies.SetCookies(codec.url, httpResponse.Cookies()) + } + + codec.responses[request.Seq] = httpResponse + codec.ready <- request.Seq + + return nil +} + +func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) { + seq := <-codec.ready + httpResponse := codec.responses[seq] + + if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { + return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode) + } + + respData, err := ioutil.ReadAll(httpResponse.Body) + + if err != nil { + return err + } + + httpResponse.Body.Close() + + resp := NewResponse(respData) + + if resp.Failed() { + response.Error = fmt.Sprintf("%v", resp.Err()) + } + + codec.response = resp + + response.Seq = seq + delete(codec.responses, seq) + + return nil +} + +func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) { + if v == nil { + return nil + } + + if err = codec.response.Unmarshal(v); err != nil { + return err + } + + return nil +} + +func (codec *clientCodec) Close() error { + transport := codec.httpClient.Transport.(*http.Transport) + transport.CloseIdleConnections() + return nil +} + +// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service. +func NewClient(requrl string, transport http.RoundTripper) (*Client, error) { + if transport == nil { + transport = http.DefaultTransport + } + + httpClient := &http.Client{Transport: transport} + + jar, err := cookiejar.New(nil) + + if err != nil { + return nil, err + } + + u, err := url.Parse(requrl) + + if err != nil { + return nil, err + } + + codec := clientCodec{ + url: u, + httpClient: httpClient, + ready: make(chan uint64), + responses: make(map[uint64]*http.Response), + cookies: jar, + } + + return &Client{rpc.NewClientWithCodec(&codec)}, nil +} diff --git a/vendor/github.com/kolo/xmlrpc/decoder.go b/vendor/github.com/kolo/xmlrpc/decoder.go new file mode 100644 index 000000000000..b73955978ece --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/decoder.go @@ -0,0 +1,449 @@ +package xmlrpc + +import ( + "bytes" + "encoding/xml" + "errors" + "fmt" + "io" + "reflect" + "strconv" + "strings" + "time" +) + +const iso8601 = "20060102T15:04:05" + +var ( + // CharsetReader is a function to generate reader which converts a non UTF-8 + // charset into UTF-8. + CharsetReader func(string, io.Reader) (io.Reader, error) + + invalidXmlError = errors.New("invalid xml") +) + +type TypeMismatchError string + +func (e TypeMismatchError) Error() string { return string(e) } + +type decoder struct { + *xml.Decoder +} + +func unmarshal(data []byte, v interface{}) (err error) { + dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))} + + if CharsetReader != nil { + dec.CharsetReader = CharsetReader + } + + var tok xml.Token + for { + if tok, err = dec.Token(); err != nil { + return err + } + + if t, ok := tok.(xml.StartElement); ok { + if t.Name.Local == "value" { + val := reflect.ValueOf(v) + if val.Kind() != reflect.Ptr { + return errors.New("non-pointer value passed to unmarshal") + } + if err = dec.decodeValue(val.Elem()); err != nil { + return err + } + + break + } + } + } + + // read until end of document + err = dec.Skip() + if err != nil && err != io.EOF { + return err + } + + return nil +} + +func (dec *decoder) decodeValue(val reflect.Value) error { + var tok xml.Token + var err error + + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + + var typeName string + for { + if tok, err = dec.Token(); err != nil { + return err + } + + if t, ok := tok.(xml.EndElement); ok { + if t.Name.Local == "value" { + return nil + } else { + return invalidXmlError + } + } + + if t, ok := tok.(xml.StartElement); ok { + typeName = t.Name.Local + break + } + + // Treat value data without type identifier as string + if t, ok := tok.(xml.CharData); ok { + if value := strings.TrimSpace(string(t)); value != "" { + if err = checkType(val, reflect.String); err != nil { + return err + } + + val.SetString(value) + return nil + } + } + } + + switch typeName { + case "struct": + ismap := false + pmap := val + valType := val.Type() + + if err = checkType(val, reflect.Struct); err != nil { + if checkType(val, reflect.Map) == nil { + if valType.Key().Kind() != reflect.String { + return fmt.Errorf("only maps with string key type can be unmarshalled") + } + ismap = true + } else if checkType(val, reflect.Interface) == nil && val.IsNil() { + var dummy map[string]interface{} + pmap = reflect.New(reflect.TypeOf(dummy)).Elem() + valType = pmap.Type() + ismap = true + } else { + return err + } + } + + var fields map[string]reflect.Value + + if !ismap { + fields = make(map[string]reflect.Value) + + for i := 0; i < valType.NumField(); i++ { + field := valType.Field(i) + fieldVal := val.FieldByName(field.Name) + + if fieldVal.CanSet() { + if fn := field.Tag.Get("xmlrpc"); fn != "" { + fields[fn] = fieldVal + } else { + fields[field.Name] = fieldVal + } + } + } + } else { + // Create initial empty map + pmap.Set(reflect.MakeMap(valType)) + } + + // Process struct members. + StructLoop: + for { + if tok, err = dec.Token(); err != nil { + return err + } + switch t := tok.(type) { + case xml.StartElement: + if t.Name.Local != "member" { + return invalidXmlError + } + + tagName, fieldName, err := dec.readTag() + if err != nil { + return err + } + if tagName != "name" { + return invalidXmlError + } + + var fv reflect.Value + ok := true + + if !ismap { + fv, ok = fields[string(fieldName)] + } else { + fv = reflect.New(valType.Elem()) + } + + if ok { + for { + if tok, err = dec.Token(); err != nil { + return err + } + if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" { + if err = dec.decodeValue(fv); err != nil { + return err + } + + // + if err = dec.Skip(); err != nil { + return err + } + + break + } + } + } + + // + if err = dec.Skip(); err != nil { + return err + } + + if ismap { + pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv)) + val.Set(pmap) + } + case xml.EndElement: + break StructLoop + } + } + case "array": + pslice := val + if checkType(val, reflect.Interface) == nil && val.IsNil() { + var dummy []interface{} + pslice = reflect.New(reflect.TypeOf(dummy)).Elem() + } else if err = checkType(val, reflect.Slice); err != nil { + return err + } + + ArrayLoop: + for { + if tok, err = dec.Token(); err != nil { + return err + } + + switch t := tok.(type) { + case xml.StartElement: + if t.Name.Local != "data" { + return invalidXmlError + } + + slice := reflect.MakeSlice(pslice.Type(), 0, 0) + + DataLoop: + for { + if tok, err = dec.Token(); err != nil { + return err + } + + switch tt := tok.(type) { + case xml.StartElement: + if tt.Name.Local != "value" { + return invalidXmlError + } + + v := reflect.New(pslice.Type().Elem()) + if err = dec.decodeValue(v); err != nil { + return err + } + + slice = reflect.Append(slice, v.Elem()) + + // + if err = dec.Skip(); err != nil { + return err + } + case xml.EndElement: + pslice.Set(slice) + val.Set(pslice) + break DataLoop + } + } + case xml.EndElement: + break ArrayLoop + } + } + default: + if tok, err = dec.Token(); err != nil { + return err + } + + var data []byte + + switch t := tok.(type) { + case xml.EndElement: + return nil + case xml.CharData: + data = []byte(t.Copy()) + default: + return invalidXmlError + } + + switch typeName { + case "int", "i4", "i8": + if checkType(val, reflect.Interface) == nil && val.IsNil() { + i, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return err + } + + pi := reflect.New(reflect.TypeOf(i)).Elem() + pi.SetInt(i) + val.Set(pi) + } else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil { + return err + } else { + i, err := strconv.ParseInt(string(data), 10, val.Type().Bits()) + if err != nil { + return err + } + + val.SetInt(i) + } + case "string", "base64": + str := string(data) + if checkType(val, reflect.Interface) == nil && val.IsNil() { + pstr := reflect.New(reflect.TypeOf(str)).Elem() + pstr.SetString(str) + val.Set(pstr) + } else if err = checkType(val, reflect.String); err != nil { + return err + } else { + val.SetString(str) + } + case "dateTime.iso8601": + t, err := time.Parse(iso8601, string(data)) + if err != nil { + return err + } + + if checkType(val, reflect.Interface) == nil && val.IsNil() { + ptime := reflect.New(reflect.TypeOf(t)).Elem() + ptime.Set(reflect.ValueOf(t)) + val.Set(ptime) + } else if _, ok := val.Interface().(time.Time); !ok { + return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind())) + } else { + val.Set(reflect.ValueOf(t)) + } + case "boolean": + v, err := strconv.ParseBool(string(data)) + if err != nil { + return err + } + + if checkType(val, reflect.Interface) == nil && val.IsNil() { + pv := reflect.New(reflect.TypeOf(v)).Elem() + pv.SetBool(v) + val.Set(pv) + } else if err = checkType(val, reflect.Bool); err != nil { + return err + } else { + val.SetBool(v) + } + case "double": + if checkType(val, reflect.Interface) == nil && val.IsNil() { + i, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return err + } + + pdouble := reflect.New(reflect.TypeOf(i)).Elem() + pdouble.SetFloat(i) + val.Set(pdouble) + } else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil { + return err + } else { + i, err := strconv.ParseFloat(string(data), val.Type().Bits()) + if err != nil { + return err + } + + val.SetFloat(i) + } + default: + return errors.New("unsupported type") + } + + // + if err = dec.Skip(); err != nil { + return err + } + } + + return nil +} + +func (dec *decoder) readTag() (string, []byte, error) { + var tok xml.Token + var err error + + var name string + for { + if tok, err = dec.Token(); err != nil { + return "", nil, err + } + + if t, ok := tok.(xml.StartElement); ok { + name = t.Name.Local + break + } + } + + value, err := dec.readCharData() + if err != nil { + return "", nil, err + } + + return name, value, dec.Skip() +} + +func (dec *decoder) readCharData() ([]byte, error) { + var tok xml.Token + var err error + + if tok, err = dec.Token(); err != nil { + return nil, err + } + + if t, ok := tok.(xml.CharData); ok { + return []byte(t.Copy()), nil + } else { + return nil, invalidXmlError + } +} + +func checkType(val reflect.Value, kinds ...reflect.Kind) error { + if len(kinds) == 0 { + return nil + } + + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + + match := false + + for _, kind := range kinds { + if val.Kind() == kind { + match = true + break + } + } + + if !match { + return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v", + val.Kind(), kinds[0])) + } + + return nil +} diff --git a/vendor/github.com/kolo/xmlrpc/encoder.go b/vendor/github.com/kolo/xmlrpc/encoder.go new file mode 100644 index 000000000000..bb1285ff7adc --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/encoder.go @@ -0,0 +1,164 @@ +package xmlrpc + +import ( + "bytes" + "encoding/xml" + "fmt" + "reflect" + "strconv" + "time" +) + +type encodeFunc func(reflect.Value) ([]byte, error) + +func marshal(v interface{}) ([]byte, error) { + if v == nil { + return []byte{}, nil + } + + val := reflect.ValueOf(v) + return encodeValue(val) +} + +func encodeValue(val reflect.Value) ([]byte, error) { + var b []byte + var err error + + if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface { + if val.IsNil() { + return []byte(""), nil + } + + val = val.Elem() + } + + switch val.Kind() { + case reflect.Struct: + switch val.Interface().(type) { + case time.Time: + t := val.Interface().(time.Time) + b = []byte(fmt.Sprintf("%s", t.Format(iso8601))) + default: + b, err = encodeStruct(val) + } + case reflect.Map: + b, err = encodeMap(val) + case reflect.Slice: + b, err = encodeSlice(val) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + b = []byte(fmt.Sprintf("%s", strconv.FormatInt(val.Int(), 10))) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + b = []byte(fmt.Sprintf("%s", strconv.FormatUint(val.Uint(), 10))) + case reflect.Float32, reflect.Float64: + b = []byte(fmt.Sprintf("%s", + strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))) + case reflect.Bool: + if val.Bool() { + b = []byte("1") + } else { + b = []byte("0") + } + case reflect.String: + var buf bytes.Buffer + + xml.Escape(&buf, []byte(val.String())) + + if _, ok := val.Interface().(Base64); ok { + b = []byte(fmt.Sprintf("%s", buf.String())) + } else { + b = []byte(fmt.Sprintf("%s", buf.String())) + } + default: + return nil, fmt.Errorf("xmlrpc encode error: unsupported type") + } + + if err != nil { + return nil, err + } + + return []byte(fmt.Sprintf("%s", string(b))), nil +} + +func encodeStruct(val reflect.Value) ([]byte, error) { + var b bytes.Buffer + + b.WriteString("") + + t := val.Type() + for i := 0; i < t.NumField(); i++ { + b.WriteString("") + f := t.Field(i) + + name := f.Tag.Get("xmlrpc") + if name == "" { + name = f.Name + } + b.WriteString(fmt.Sprintf("%s", name)) + + p, err := encodeValue(val.FieldByName(f.Name)) + if err != nil { + return nil, err + } + b.Write(p) + + b.WriteString("") + } + + b.WriteString("") + + return b.Bytes(), nil +} + +func encodeMap(val reflect.Value) ([]byte, error) { + var t = val.Type() + + if t.Key().Kind() != reflect.String { + return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported") + } + + var b bytes.Buffer + + b.WriteString("") + + keys := val.MapKeys() + + for i := 0; i < val.Len(); i++ { + key := keys[i] + kval := val.MapIndex(key) + + b.WriteString("") + b.WriteString(fmt.Sprintf("%s", key.String())) + + p, err := encodeValue(kval) + + if err != nil { + return nil, err + } + + b.Write(p) + b.WriteString("") + } + + b.WriteString("") + + return b.Bytes(), nil +} + +func encodeSlice(val reflect.Value) ([]byte, error) { + var b bytes.Buffer + + b.WriteString("") + + for i := 0; i < val.Len(); i++ { + p, err := encodeValue(val.Index(i)) + if err != nil { + return nil, err + } + + b.Write(p) + } + + b.WriteString("") + + return b.Bytes(), nil +} diff --git a/vendor/github.com/kolo/xmlrpc/request.go b/vendor/github.com/kolo/xmlrpc/request.go new file mode 100644 index 000000000000..acb8251b2b3c --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/request.go @@ -0,0 +1,57 @@ +package xmlrpc + +import ( + "bytes" + "fmt" + "net/http" +) + +func NewRequest(url string, method string, args interface{}) (*http.Request, error) { + var t []interface{} + var ok bool + if t, ok = args.([]interface{}); !ok { + if args != nil { + t = []interface{}{args} + } + } + + body, err := EncodeMethodCall(method, t...) + if err != nil { + return nil, err + } + + request, err := http.NewRequest("POST", url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + request.Header.Set("Content-Type", "text/xml") + request.Header.Set("Content-Length", fmt.Sprintf("%d", len(body))) + + return request, nil +} + +func EncodeMethodCall(method string, args ...interface{}) ([]byte, error) { + var b bytes.Buffer + b.WriteString(``) + b.WriteString(fmt.Sprintf("%s", method)) + + if args != nil { + b.WriteString("") + + for _, arg := range args { + p, err := marshal(arg) + if err != nil { + return nil, err + } + + b.WriteString(fmt.Sprintf("%s", string(p))) + } + + b.WriteString("") + } + + b.WriteString("") + + return b.Bytes(), nil +} diff --git a/vendor/github.com/kolo/xmlrpc/response.go b/vendor/github.com/kolo/xmlrpc/response.go new file mode 100644 index 000000000000..6742a1c74860 --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/response.go @@ -0,0 +1,52 @@ +package xmlrpc + +import ( + "regexp" +) + +var ( + faultRx = regexp.MustCompile(`(\s|\S)+`) +) + +type failedResponse struct { + Code int `xmlrpc:"faultCode"` + Error string `xmlrpc:"faultString"` +} + +func (r *failedResponse) err() error { + return &xmlrpcError{ + code: r.Code, + err: r.Error, + } +} + +type Response struct { + data []byte +} + +func NewResponse(data []byte) *Response { + return &Response{ + data: data, + } +} + +func (r *Response) Failed() bool { + return faultRx.Match(r.data) +} + +func (r *Response) Err() error { + failedResp := new(failedResponse) + if err := unmarshal(r.data, failedResp); err != nil { + return err + } + + return failedResp.err() +} + +func (r *Response) Unmarshal(v interface{}) error { + if err := unmarshal(r.data, v); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/kolo/xmlrpc/test_server.rb b/vendor/github.com/kolo/xmlrpc/test_server.rb new file mode 100644 index 000000000000..1b1ff8760f79 --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/test_server.rb @@ -0,0 +1,25 @@ +# encoding: utf-8 + +require "xmlrpc/server" + +class Service + def time + Time.now + end + + def upcase(s) + s.upcase + end + + def sum(x, y) + x + y + end + + def error + raise XMLRPC::FaultException.new(500, "Server error") + end +end + +server = XMLRPC::Server.new 5001, 'localhost' +server.add_handler "service", Service.new +server.serve diff --git a/vendor/github.com/kolo/xmlrpc/xmlrpc.go b/vendor/github.com/kolo/xmlrpc/xmlrpc.go new file mode 100644 index 000000000000..8766403afeff --- /dev/null +++ b/vendor/github.com/kolo/xmlrpc/xmlrpc.go @@ -0,0 +1,19 @@ +package xmlrpc + +import ( + "fmt" +) + +// xmlrpcError represents errors returned on xmlrpc request. +type xmlrpcError struct { + code int + err string +} + +// Error() method implements Error interface +func (e *xmlrpcError) Error() string { + return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code) +} + +// Base64 represents value in base64 encoding +type Base64 string