From 5c4e45b07d13468e28d9a01feec21c302f7ba8d1 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Sun, 19 May 2024 17:51:10 +0200 Subject: [PATCH] Menu: Add new XML-RPC methods --- fixtures/copy-menu-req.xml | 21 ++++ fixtures/copy-menu-res.xml | 10 ++ fixtures/find-menu-names-req.xml | 28 +++++ fixtures/find-menu-names-res.xml | 16 +++ fixtures/find-menu-req.xml | 28 +++++ fixtures/find-menu-res.xml | 151 +++++++++++++++++++++++++ fixtures/get-item-names-menu-req.xml | 11 ++ fixtures/get-item-names-menu-res.xml | 16 +++ fixtures/get-menu-handle-req.xml | 16 +++ fixtures/get-menu-handle-res.xml | 10 ++ fixtures/get-menu-req.xml | 16 +++ fixtures/get-menu-res.xml | 139 +++++++++++++++++++++++ fixtures/get-menus-req.xml | 16 +++ fixtures/get-menus-res.xml | 151 +++++++++++++++++++++++++ fixtures/get-menus-since-req.xml | 11 ++ fixtures/get-menus-since-res.xml | 151 +++++++++++++++++++++++++ fixtures/rename-menu-req.xml | 21 ++++ fixtures/rename-menu-res.xml | 10 ++ fixtures/save-menu-req.xml | 21 ++++ fixtures/save-menu-res.xml | 10 ++ menu.go | 158 +++++++++++++++++++++++++-- menu_test.go | 108 ++++++++++++++++-- 22 files changed, 1102 insertions(+), 17 deletions(-) diff --git a/fixtures/copy-menu-req.xml b/fixtures/copy-menu-req.xml index e69de29..927769c 100644 --- a/fixtures/copy-menu-req.xml +++ b/fixtures/copy-menu-req.xml @@ -0,0 +1,21 @@ + + + copy_menu + + + + menu::testmenu + + + + + testmenu2 + + + + + securetoken99 + + + + diff --git a/fixtures/copy-menu-res.xml b/fixtures/copy-menu-res.xml index e69de29..df50d46 100644 --- a/fixtures/copy-menu-res.xml +++ b/fixtures/copy-menu-res.xml @@ -0,0 +1,10 @@ + + + + + + 1 + + + + diff --git a/fixtures/find-menu-names-req.xml b/fixtures/find-menu-names-req.xml index e69de29..d4cdcbb 100644 --- a/fixtures/find-menu-names-req.xml +++ b/fixtures/find-menu-names-req.xml @@ -0,0 +1,28 @@ + + + find_menu + + + + + + name + + testmenu + + + + + + + + 0 + + + + + securetoken99 + + + + diff --git a/fixtures/find-menu-names-res.xml b/fixtures/find-menu-names-res.xml index e69de29..e0a5ab5 100644 --- a/fixtures/find-menu-names-res.xml +++ b/fixtures/find-menu-names-res.xml @@ -0,0 +1,16 @@ + + + + + + + + + testmenu + + + + + + + diff --git a/fixtures/find-menu-req.xml b/fixtures/find-menu-req.xml index e69de29..8e1786e 100644 --- a/fixtures/find-menu-req.xml +++ b/fixtures/find-menu-req.xml @@ -0,0 +1,28 @@ + + + find_menu + + + + + + name + + testmenu + + + + + + + + 1 + + + + + securetoken99 + + + + diff --git a/fixtures/find-menu-res.xml b/fixtures/find-menu-res.xml index e69de29..23d4e79 100644 --- a/fixtures/find-menu-res.xml +++ b/fixtures/find-menu-res.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + parent + + + + + + depth + + 0 + + + + children + + + + + + + + + ctime + + 1716132890.5260634 + + + + mtime + + 1716132890.5260634 + + + + uid + + ecfb2f9cb717495988bee1d9d1c79504 + + + + name + + testmenu + + + + comment + + + + + + kernel_options + + + + + + + kernel_options_post + + + + + + + autoinstall_meta + + + + + + + fetchable_files + + + + + + + boot_files + + + + + + + template_files + + + + + + + owners + + <<inherit>> + + + + mgmt_classes + + + + + + + + + mgmt_parameters + + + + + + + is_subobject + + 0 + + + + display_name + + + + + + ks_meta + + + + + + + + + + + + + diff --git a/fixtures/get-item-names-menu-req.xml b/fixtures/get-item-names-menu-req.xml index e69de29..c97d086 100644 --- a/fixtures/get-item-names-menu-req.xml +++ b/fixtures/get-item-names-menu-req.xml @@ -0,0 +1,11 @@ + + + get_item_names + + + + menu + + + + diff --git a/fixtures/get-item-names-menu-res.xml b/fixtures/get-item-names-menu-res.xml index e69de29..e0a5ab5 100644 --- a/fixtures/get-item-names-menu-res.xml +++ b/fixtures/get-item-names-menu-res.xml @@ -0,0 +1,16 @@ + + + + + + + + + testmenu + + + + + + + diff --git a/fixtures/get-menu-handle-req.xml b/fixtures/get-menu-handle-req.xml index e69de29..fa4ce60 100644 --- a/fixtures/get-menu-handle-req.xml +++ b/fixtures/get-menu-handle-req.xml @@ -0,0 +1,16 @@ + + + get_menu_handle + + + + testmenu + + + + + securetoken99 + + + + diff --git a/fixtures/get-menu-handle-res.xml b/fixtures/get-menu-handle-res.xml index e69de29..708a301 100644 --- a/fixtures/get-menu-handle-res.xml +++ b/fixtures/get-menu-handle-res.xml @@ -0,0 +1,10 @@ + + + + + + menu::testmenu + + + + diff --git a/fixtures/get-menu-req.xml b/fixtures/get-menu-req.xml index e69de29..87a3fba 100644 --- a/fixtures/get-menu-req.xml +++ b/fixtures/get-menu-req.xml @@ -0,0 +1,16 @@ + + + get_menu + + + + testmenu + + + + + securetoken99 + + + + diff --git a/fixtures/get-menu-res.xml b/fixtures/get-menu-res.xml index e69de29..aa884f5 100644 --- a/fixtures/get-menu-res.xml +++ b/fixtures/get-menu-res.xml @@ -0,0 +1,139 @@ + + + + + + + + parent + + + + + + depth + + 0 + + + + children + + + + + + + + + ctime + + 1716132890.5260634 + + + + mtime + + 1716132890.5260634 + + + + uid + + ecfb2f9cb717495988bee1d9d1c79504 + + + + name + + testmenu + + + + comment + + + + + + kernel_options + + + + + + kernel_options_post + + + + + + autoinstall_meta + + + + + + fetchable_files + + + + + + boot_files + + + + + + template_files + + + + + + owners + + <<inherit>> + + + + mgmt_classes + + + + + + + + + mgmt_parameters + + + + + + + is_subobject + + 0 + + + + display_name + + + + + + ks_meta + + + + + + + + + + diff --git a/fixtures/get-menus-req.xml b/fixtures/get-menus-req.xml index e69de29..89b0e47 100644 --- a/fixtures/get-menus-req.xml +++ b/fixtures/get-menus-req.xml @@ -0,0 +1,16 @@ + + + get_menus + + + + -1 + + + + + securetoken99 + + + + diff --git a/fixtures/get-menus-res.xml b/fixtures/get-menus-res.xml index e69de29..23d4e79 100644 --- a/fixtures/get-menus-res.xml +++ b/fixtures/get-menus-res.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + parent + + + + + + depth + + 0 + + + + children + + + + + + + + + ctime + + 1716132890.5260634 + + + + mtime + + 1716132890.5260634 + + + + uid + + ecfb2f9cb717495988bee1d9d1c79504 + + + + name + + testmenu + + + + comment + + + + + + kernel_options + + + + + + + kernel_options_post + + + + + + + autoinstall_meta + + + + + + + fetchable_files + + + + + + + boot_files + + + + + + + template_files + + + + + + + owners + + <<inherit>> + + + + mgmt_classes + + + + + + + + + mgmt_parameters + + + + + + + is_subobject + + 0 + + + + display_name + + + + + + ks_meta + + + + + + + + + + + + + diff --git a/fixtures/get-menus-since-req.xml b/fixtures/get-menus-since-req.xml index e69de29..495a0cc 100644 --- a/fixtures/get-menus-since-req.xml +++ b/fixtures/get-menus-since-req.xml @@ -0,0 +1,11 @@ + + + get_menus_since + + + + 0 + + + + diff --git a/fixtures/get-menus-since-res.xml b/fixtures/get-menus-since-res.xml index e69de29..23d4e79 100644 --- a/fixtures/get-menus-since-res.xml +++ b/fixtures/get-menus-since-res.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + parent + + + + + + depth + + 0 + + + + children + + + + + + + + + ctime + + 1716132890.5260634 + + + + mtime + + 1716132890.5260634 + + + + uid + + ecfb2f9cb717495988bee1d9d1c79504 + + + + name + + testmenu + + + + comment + + + + + + kernel_options + + + + + + + kernel_options_post + + + + + + + autoinstall_meta + + + + + + + fetchable_files + + + + + + + boot_files + + + + + + + template_files + + + + + + + owners + + <<inherit>> + + + + mgmt_classes + + + + + + + + + mgmt_parameters + + + + + + + is_subobject + + 0 + + + + display_name + + + + + + ks_meta + + + + + + + + + + + + + diff --git a/fixtures/rename-menu-req.xml b/fixtures/rename-menu-req.xml index e69de29..9fc847b 100644 --- a/fixtures/rename-menu-req.xml +++ b/fixtures/rename-menu-req.xml @@ -0,0 +1,21 @@ + + + rename_menu + + + + menu::testmenu2 + + + + + testmenu1 + + + + + securetoken99 + + + + diff --git a/fixtures/rename-menu-res.xml b/fixtures/rename-menu-res.xml index e69de29..df50d46 100644 --- a/fixtures/rename-menu-res.xml +++ b/fixtures/rename-menu-res.xml @@ -0,0 +1,10 @@ + + + + + + 1 + + + + diff --git a/fixtures/save-menu-req.xml b/fixtures/save-menu-req.xml index e69de29..102a5d0 100644 --- a/fixtures/save-menu-req.xml +++ b/fixtures/save-menu-req.xml @@ -0,0 +1,21 @@ + + + save_menu + + + + menu::testmenu + + + + + securetoken99 + + + + + bypass + + + + diff --git a/fixtures/save-menu-res.xml b/fixtures/save-menu-res.xml index e69de29..df50d46 100644 --- a/fixtures/save-menu-res.xml +++ b/fixtures/save-menu-res.xml @@ -0,0 +1,10 @@ + + + + + + 1 + + + + diff --git a/menu.go b/menu.go index 172a729..73dec49 100644 --- a/menu.go +++ b/menu.go @@ -1,5 +1,11 @@ package cobblerclient +import ( + "fmt" + "reflect" + "time" +) + // Menu is ... type Menu struct { Item `mapstructure:",squash"` @@ -8,10 +14,141 @@ type Menu struct { DisplayName string `mapstructure:"display_name"` } +func convertRawMenu(name string, xmlrpcResult interface{}) (*Menu, error) { + var menu Menu + + if xmlrpcResult == "~" { + return nil, fmt.Errorf("menu %s not found", name) + } + + decodeResult, err := decodeCobblerItem(xmlrpcResult, &menu) + if err != nil { + return nil, err + } + + return decodeResult.(*Menu), nil +} + +func convertRawMenusList(xmlrpcResult interface{}) ([]*Menu, error) { + var menus []*Menu + + for _, d := range xmlrpcResult.([]interface{}) { + menu, err := convertRawMenu("unknown", d) + if err != nil { + return nil, err + } + menus = append(menus, menu) + } + + return menus, nil +} + +// GetMenus returns all menus in Cobbler. +func (c *Client) GetMenus() ([]*Distro, error) { + result, err := c.Call("get_menus", "-1", c.Token) + if err != nil { + return nil, err + } + + return convertRawDistrosList(result) +} + +// GetMenu returns a single menu obtained by its name. +func (c *Client) GetMenu(name string) (*Menu, error) { + result, err := c.Call("get_menu", name, c.Token) + if err != nil { + return nil, err + } + + return convertRawMenu(name, result) +} + +// CreateMenu creates a menu. +func (c *Client) CreateMenu(menu Menu) (*Menu, error) { + // Make sure a menu with the same name does not already exist + if _, err := c.GetMenu(menu.Name); err == nil { + return nil, fmt.Errorf("a Menu with the name %s already exists", menu.Name) + } + + result, err := c.Call("new_menu", c.Token) + if err != nil { + return nil, err + } + newID := result.(string) + + item := reflect.ValueOf(&menu).Elem() + if err := c.updateCobblerFields("menu", item, newID); err != nil { + return nil, err + } + + if err = c.SaveMenu(newID, "new"); err != nil { + return nil, err + } + + return c.GetMenu(menu.Name) +} + +// UpdateMenu updates a single menu. +func (c *Client) UpdateMenu(menu *Menu) error { + item := reflect.ValueOf(menu).Elem() + id, err := c.GetItemHandle("menu", menu.Name) + if err != nil { + return err + } + + if err := c.updateCobblerFields("menu", item, id); err != nil { + return err + } + + if err = c.SaveMenu(id, "bypass"); err != nil { + return err + } + + return nil +} + +// ListMenuNames is returning a list of all menu names currently available in Cobbler. +func (c *Client) ListMenuNames() ([]string, error) { + return c.GetItemNames("menu") +} + // FindMenu is ... func (c *Client) FindMenu(criteria map[string]interface{}) ([]*Menu, error) { - _, err := c.Call("find_menu") - return nil, err + var menus []*Menu + + result, err := c.Call("find_menu", criteria, true, c.Token) + if err != nil { + return nil, err + } + + for _, d := range result.([]interface{}) { + var menu Menu + decodedResult, err := decodeCobblerItem(d, &menu) + if err != nil { + return nil, err + } + + menus = append(menus, decodedResult.(*Menu)) + } + + return menus, nil +} + +// FindMenuNames is searching for one or more menus by any of its attributes. +func (c *Client) FindMenuNames(criteria map[string]interface{}) ([]string, error) { + var result []string + + resultUnmarshalled, err := c.Call("find_menu", criteria, false, c.Token) + + if err != nil { + return nil, err + } + + for _, name := range resultUnmarshalled.([]interface{}) { + result = append(result, name.(string)) + } + + return result, nil } // GetMenuHandle gets the internal ID of a Cobbler item. @@ -19,9 +156,8 @@ func (c *Client) GetMenuHandle(name string) (string, error) { result, err := c.Call("get_menu_handle", name, c.Token) if err != nil { return "", err - } else { - return result.(string), err } + return result.(string), err } // CopyMenu is ... @@ -31,9 +167,13 @@ func (c *Client) CopyMenu(objectId, newName string) error { } // GetMenusSince is ... -func (c *Client) GetMenusSince() error { - _, err := c.Call("get_menus_since") - return err +func (c *Client) GetMenusSince(mtime time.Time) ([]*Menu, error) { + result, err := c.Call("get_menus_since", float64(mtime.Unix())) + if err != nil { + return nil, err + } + + return convertRawMenusList(result) } // GetMenuAsRendered is ... @@ -43,8 +183,8 @@ func (c *Client) GetMenuAsRendered() error { } // SaveMenu is ... -func (c *Client) SaveMenu(objectId, token, editmode string) error { - _, err := c.Call("save_menu", objectId, token, editmode) +func (c *Client) SaveMenu(objectId, editmode string) error { + _, err := c.Call("save_menu", objectId, c.Token, editmode) return err } diff --git a/menu_test.go b/menu_test.go index cbc6ba1..a319772 100644 --- a/menu_test.go +++ b/menu_test.go @@ -1,17 +1,72 @@ +/* +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 ( "testing" + "time" "github.com/ContainerSolutions/go-utils" ) -func TestDeleteMenu(t *testing.T) {} +func TestGetMenus(t *testing.T) { + c := createStubHTTPClient(t, "get-menus-req.xml", "get-menus-res.xml") + menus, err := c.GetMenus() + utils.FailOnError(t, err) + + if len(menus) != 1 { + t.Errorf("Wrong number of menus returned.") + } +} + +func TestGetMenu(t *testing.T) { + c := createStubHTTPClient(t, "get-menu-req.xml", "get-menu-res.xml") + menu, err := c.GetMenu("testmenu") + utils.FailOnError(t, err) + + if menu.Name != "testmenu" { + t.Errorf("Wrong menu returned.") + } +} + +func TestListMenuNames(t *testing.T) { + c := createStubHTTPClient(t, "get-item-names-menu-req.xml", "get-item-names-menu-res.xml") + menus, err := c.ListMenuNames() + utils.FailOnError(t, err) + + if len(menus) != 1 { + t.Errorf("Wrong number of menus returned.") + } +} + +func TestGetMenusSince(t *testing.T) { + c := createStubHTTPClient(t, "get-menus-since-req.xml", "get-menus-since-res.xml") + menus, err := c.GetMenusSince(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)) + utils.FailOnError(t, err) + + if len(menus) != 1 { + t.Errorf("Wrong number of menus returned.") + } +} func TestFindMenu(t *testing.T) { c := createStubHTTPClient(t, "find-menu-req.xml", "find-menu-res.xml") criteria := make(map[string]interface{}, 1) - criteria["name"] = "test" + criteria["name"] = "testmenu" menus, err := c.FindMenu(criteria) utils.FailOnError(t, err) @@ -20,14 +75,51 @@ func TestFindMenu(t *testing.T) { } } -func TestGetMenuHandle(t *testing.T) {} +func TestFindMenuNames(t *testing.T) { + c := createStubHTTPClient(t, "find-menu-names-req.xml", "find-menu-names-res.xml") + criteria := make(map[string]interface{}, 1) + criteria["name"] = "testmenu" + menus, err := c.FindMenuNames(criteria) + utils.FailOnError(t, err) + + if len(menus) != 1 { + t.Error("Wrong number of menus returned.") + } +} + +func TestSaveMenu(t *testing.T) { + c := createStubHTTPClient(t, "save-menu-req.xml", "save-menu-res.xml") + err := c.SaveMenu("menu::testmenu", "bypass") + utils.FailOnError(t, err) +} -func TestCopyMenu(t *testing.T) {} +func TestCopyMenu(t *testing.T) { + c := createStubHTTPClient(t, "copy-menu-req.xml", "copy-menu-res.xml") + err := c.CopyMenu("menu::testmenu", "testmenu2") + utils.FailOnError(t, err) +} -func TestGetMenusSince(t *testing.T) {} +func TestRenameMenu(t *testing.T) { + c := createStubHTTPClient(t, "rename-menu-req.xml", "rename-menu-res.xml") + err := c.RenameMenu("menu::testmenu2", "testmenu1") + utils.FailOnError(t, err) +} -func TestGetMenuAsRendered(t *testing.T) {} +func TestGetMenuHandle(t *testing.T) { + c := createStubHTTPClient(t, "get-menu-handle-req.xml", "get-menu-handle-res.xml") + res, err := c.GetMenuHandle("testmenu") + utils.FailOnError(t, err) -func TestSaveMenu(t *testing.T) {} + if res != "menu::testmenu" { + t.Error("Wrong object id returned.") + } +} -func TestRenameMenu(t *testing.T) {} +/* + * NOTE: We're skipping the testing of CREATE, UPDATE, DELETE methods for now because + * the current implementation of the StubHTTPClient does not allow + * buffered mock responses so as soon as the method makes the second + * call to Cobbler it'll fail. + * This is a system test, so perhaps we can run Cobbler in a Docker container + * and take it from there. + */