From cbc07cd6f343240e33611476990d0aa805366097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wielgosz?= Date: Tue, 20 Feb 2018 08:12:01 +0100 Subject: [PATCH 1/2] User object basic implementation --- iapi/structs.go | 14 ++++++++ iapi/users.go | 80 ++++++++++++++++++++++++++++++++++++++++++++ iapi/users_test.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 iapi/users.go create mode 100644 iapi/users_test.go diff --git a/iapi/structs.go b/iapi/structs.go index 3e31bba..d4f72e2 100644 --- a/iapi/structs.go +++ b/iapi/structs.go @@ -125,3 +125,17 @@ type APIStatus struct { } `json:"status"` } `json:"results"` } + +// UserStruct is a struct used to store results from an Icinga2 User API Call. The content are also used to generate the JSON for the CreateUser call +type UserStruct struct { + Name string `json:"name"` + Type string `json:"type"` + Attrs UserAttrs `json:"attrs"` + Meta struct{} `json:"meta"` + Joins struct{} `json:"stuct"` +} + +// UserAttrs This is struct lists the attributes that can be set during a CreateUser call. The contents of the struct is converted into JSON +type UserAttrs struct { + Email string `json:"email"` +} diff --git a/iapi/users.go b/iapi/users.go new file mode 100644 index 0000000..da14caf --- /dev/null +++ b/iapi/users.go @@ -0,0 +1,80 @@ +package iapi + +import ( + "encoding/json" + "fmt" +) + +// GetUser ... +func (server *Server) GetUser(name string) ([]UserStruct, error) { + + var users []UserStruct + results, err := server.NewAPIRequest("GET", "/objects/users/"+name, nil) + if err != nil { + return nil, err + } + + // Contents of the results is an interface object. Need to convert it to json first. + jsonStr, marshalErr := json.Marshal(results.Results) + if marshalErr != nil { + return nil, marshalErr + } + + // then the JSON can be pushed into the appropriate struct. + // Note : Results is a slice so much push into a slice. + + if unmarshalErr := json.Unmarshal(jsonStr, &users); unmarshalErr != nil { + return nil, unmarshalErr + } + + return users, err +} + +// CreateUser ... +func (server *Server) CreateUser(name, email string) ([]UserStruct, error) { + + var newAttrs UserAttrs + newAttrs.Email = email + + var newUser UserStruct + newUser.Name = name + newUser.Type = "User" + newUser.Attrs = newAttrs + + // Create JSON from completed struct + payloadJSON, marshalErr := json.Marshal(newUser) + if marshalErr != nil { + return nil, marshalErr + } + + //fmt.Printf(" %s\n", payloadJSON) + + // Make the API request to create the users. + results, err := server.NewAPIRequest("PUT", "/objects/users/"+name, []byte(payloadJSON)) + if err != nil { + return nil, err + } + + if results.Code == 200 { + users, err := server.GetUser(name) + return users, err + } + + return nil, fmt.Errorf("%s", results.ErrorString) + +} + +// DeleteUser ... +func (server *Server) DeleteUser(name string) error { + + results, err := server.NewAPIRequest("DELETE", "/objects/users/"+name+"?cascade=1", nil) + if err != nil { + return err + } + + if results.Code == 200 { + return nil + } else { + return fmt.Errorf("%s", results.ErrorString) + } +} diff --git a/iapi/users_test.go b/iapi/users_test.go new file mode 100644 index 0000000..a3fdeba --- /dev/null +++ b/iapi/users_test.go @@ -0,0 +1,83 @@ +package iapi + +import ( + "strings" + "testing" +) + +func TestGetValidUser(t *testing.T) { + + username := "valid-user" + + _, err := Icinga2_Server.GetUser(username) + + if err != nil { + t.Error(err) + } +} + +func TestGetInvalidUser(t *testing.T) { + + username := "invalid-user" + _, err := Icinga2_Server.GetUser(username) + if err != nil { + t.Error(err) + } +} + +func TestCreateSimpleUser(t *testing.T) { + + username := "test-user" + + _, err := Icinga2_Server.CreateUser(username, "") + + if err != nil { + t.Error(err) + } +} + +func TestCreateUserAlreadyExists(t *testing.T) { + + username := "test-user" + + _, err := Icinga2_Server.CreateUser(username, "") + + if !strings.HasSuffix(err.Error(), " already exists.") { + t.Error(err) + } +} + +func TestCreateUserWithEmail(t *testing.T) { + + username := "test-user-with-email" + Email := "email@example.com" + + _, err := Icinga2_Server.CreateUser(username, Email) + if err != nil { + t.Error(err) + } + + // Delete user after creating it. + deleteErr := Icinga2_Server.DeleteUser(username) + if deleteErr != nil { + t.Error(err) + } +} + +func TestDeleteUser(t *testing.T) { + + username := "test-user" + + err := Icinga2_Server.DeleteUser(username) + if err != nil { + t.Error(err) + } +} + +func TestDeleteUserDNE(t *testing.T) { + username := "test-user" + err := Icinga2_Server.DeleteUser(username) + if err.Error() != "No objects found." { + t.Error(err) + } +} \ No newline at end of file From 2577871081aa38b8ce6f20fbeb30b5da6ad0253f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wielgosz?= Date: Tue, 20 Feb 2018 08:13:01 +0100 Subject: [PATCH 2/2] Notification object implementation --- iapi/notifications.go | 85 ++++++++++++++ iapi/notifications_test.go | 233 +++++++++++++++++++++++++++++++++++++ iapi/structs.go | 17 +++ 3 files changed, 335 insertions(+) create mode 100644 iapi/notifications.go create mode 100644 iapi/notifications_test.go diff --git a/iapi/notifications.go b/iapi/notifications.go new file mode 100644 index 0000000..61e4e17 --- /dev/null +++ b/iapi/notifications.go @@ -0,0 +1,85 @@ +package iapi + +import ( + "encoding/json" + "fmt" +) + +// GetNotification ... +func (server *Server) GetNotification(name string) ([]NotificationStruct, error) { + + var notifications []NotificationStruct + results, err := server.NewAPIRequest("GET", "/objects/notifications/"+name, nil) + if err != nil { + return nil, err + } + + // Contents of the results is an interface object. Need to convert it to json first. + jsonStr, marshalErr := json.Marshal(results.Results) + if marshalErr != nil { + return nil, marshalErr + } + + // then the JSON can be pushed into the appropriate struct. + // Note : Results is a slice so much push into a slice. + + if unmarshalErr := json.Unmarshal(jsonStr, ¬ifications); unmarshalErr != nil { + return nil, unmarshalErr + } + + return notifications, err +} + +// CreateNotification ... +func (server *Server) CreateNotification(name, hostname, command, servicename string, interval int, users []string, vars map[string]string, templates []string) ([]NotificationStruct, error) { + + var newAttrs NotificationAttrs + newAttrs.Command = command + newAttrs.Users = users + newAttrs.Servicename = servicename + newAttrs.Interval = interval + newAttrs.Vars = vars + newAttrs.Templates = templates + + var newNotification NotificationStruct + newNotification.Name = name + newNotification.Type = "Notification" + newNotification.Attrs = newAttrs + + // Create JSON from completed struct + payloadJSON, marshalErr := json.Marshal(newNotification) + if marshalErr != nil { + return nil, marshalErr + } + + //fmt.Printf(" %s\n", payloadJSON) + + // Make the API request to create the hosts. + results, err := server.NewAPIRequest("PUT", "/objects/notifications/"+name, []byte(payloadJSON)) + if err != nil { + return nil, err + } + + if results.Code == 200 { + notifications, err := server.GetNotification(name) + return notifications, err + } + + return nil, fmt.Errorf("%s", results.ErrorString) +} + +// DeleteNotification ... +func (server *Server) DeleteNotification(name string) error { + + results, err := server.NewAPIRequest("DELETE", "/objects/notifications/"+name+"?cascade=1", nil) + if err != nil { + return err + } + + if results.Code == 200 { + return nil + } else { + return fmt.Errorf("%s", results.ErrorString) + } + +} diff --git a/iapi/notifications_test.go b/iapi/notifications_test.go new file mode 100644 index 0000000..07043fa --- /dev/null +++ b/iapi/notifications_test.go @@ -0,0 +1,233 @@ +package iapi + +import ( + "strings" + "testing" +) + +func TestGetValidNotification(t *testing.T) { + + name := "valid-notification" + _, err := Icinga2_Server.GetNotification(name) + + if err != nil { + t.Error(err) + } +} + +func TestGetInvalidNotification(t *testing.T) { + + name := "invalid-notification" + + _, err := Icinga2_Server.GetNotification(name) + + if err != nil { + t.Error(err) + } +} + +func TestCreateNotificationCommandDNE(t *testing.T) { + + hostname := "host" + notificationname := hostname+"!"+hostname + command := "invalid-command" + servicename := "" + interval := 1800 + + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, nil, nil, nil) + + if !strings.Contains(err.Error(), "type 'NotificationCommand' does not exist.") { + t.Error(err) + } +} + +// func TestCreateNotificationHostDNE +// Try and create a notification for a host that does not exist. +// Should fail with an error about the host not existing. +func TestCreateNotificationHostDNE(t *testing.T) { + + hostname := "host-dne" + notificationname := hostname+"!"+hostname + command := "mail-host-notification" + servicename := "" + interval := 1800 + + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, nil, nil, nil) + + if !strings.Contains(err.Error(), "type 'Host' does not exist.") { + t.Error(err) + } +} + +func TestCreateNotificationInvalidName(t *testing.T) { + + hostname := "host-dne" + notificationname := "invalid-name" + command := "mail-host-notification" + servicename := "" + interval := 1800 + + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, nil, nil, nil) + + if !strings.Contains(err.Error(), "Invalid Notification name.") { + t.Error(err) + } +} + +func TestCreateNotificationUserDNE(t *testing.T) { + + hostname := "host" + group := []string{"linux-servers"} + notificationname := hostname+"!"+hostname + command := "mail-host-notification" + servicename := "" + interval := 1800 + users := []string{"user-dne"} + + + _, _ = Icinga2_Server.CreateHost(hostname, "127.0.0.1", "hostalive", nil, nil, group) + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, users, nil, nil) + + if !strings.Contains(err.Error(), "type 'User' does not exist.") { + t.Error(err) + } +} + +func TestCreateHostNotification(t *testing.T) { + + hostname := "host" + groups := []string{"linux-servers"} + notificationname := hostname+"!"+hostname + command := "mail-host-notification" + servicename := "" + interval := 1800 + username := "user" + + _, _ = Icinga2_Server.CreateHost(hostname, "127.0.0.1", "hostalive", nil, nil, groups) + _, _ = Icinga2_Server.CreateUser(username, "user@example.com") + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, []string{username}, nil, nil) + + if err != nil { + t.Error(err) + } +} + +func TestCreateHostNotificationAlreadyExists(t *testing.T) { + + hostname := "host" + notificationname := hostname+"!"+hostname + command := "mail-host-notification" + servicename := "" + interval := 1800 + username := "user" + + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, []string{username}, nil, nil) + + if !strings.HasSuffix(err.Error(), " already exists.") { + t.Error(err) + } +} + +func TestCreateServiceNotification(t *testing.T) { + + hostname := "host" + servicename := "test" + check_command := "random" + notificationname := hostname+"!"+servicename+"!"+hostname+"-"+servicename + command := "mail-service-notification" + interval := 1800 + username := "user" + + _, _ = Icinga2_Server.CreateUser(username, "user@example.com") + _, _ = Icinga2_Server.CreateService(servicename, hostname, check_command) + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, []string{username}, nil, nil) + + if err != nil { + t.Error(err) + } +} + +func TestCreateServiceNotificationAlreadyExists(t *testing.T) { + + hostname := "host" + servicename := "test" + check_command := "random" + notificationname := hostname+"!"+servicename+"!"+hostname+"-"+servicename + command := "mail-service-notification" + interval := 1800 + username := "user" + + _, _ = Icinga2_Server.CreateUser(username, "user@example.com") + _, _ = Icinga2_Server.CreateService(servicename, hostname, check_command) + _, err := Icinga2_Server.CreateNotification(notificationname, hostname, command, servicename, interval, []string{username}, nil, nil) + + if !strings.HasSuffix(err.Error(), " already exists.") { + t.Error(err) + } +} + +func TestDeleteServiceNotification(t *testing.T) { + + hostname := "host" + servicename := "test" + notificationname := hostname+"!"+servicename+"!"+hostname+"-"+servicename + + err := Icinga2_Server.DeleteNotification(notificationname) + + if err != nil { + t.Error(err) + } +} + +func TestDeleteServiceNotificationDNE(t *testing.T) { + + hostname := "host" + servicename := "test" + notificationname := hostname+"!"+servicename+"!"+hostname+"-"+servicename + + err := Icinga2_Server.DeleteNotification(notificationname) + + if err.Error() != "No objects found." { + t.Error(err) + } +} + +func TestDeleteHostNotification(t *testing.T) { + + hostname := "host" + notificationname := hostname+"!"+hostname + username := "user" + + err := Icinga2_Server.DeleteNotification(notificationname) + _ = Icinga2_Server.DeleteHost(hostname) + _ = Icinga2_Server.DeleteUser(username) + + if err != nil { + t.Error(err) + } +} + +func TestDeleteHostNotificationDNE(t *testing.T) { + + hostname := "host" + notificationname := hostname+"!"+hostname + + err := Icinga2_Server.DeleteNotification(notificationname) + + if err.Error() != "No objects found." { + t.Error(err) + } +} + +// func TestDeleteNotificationNonAPI +// Notifications that were not created via the API, cannot be deleted via the API +// Should get an error about not being created via the API +func TestDeleteNotificationNonAPI(t *testing.T) { + + notificationname := "mail-icingaadmin" + + err := Icinga2_Server.DeleteNotification(notificationname) + if err.Error() != "No objects found." { + t.Error(err) + } +} \ No newline at end of file diff --git a/iapi/structs.go b/iapi/structs.go index d4f72e2..276b42a 100644 --- a/iapi/structs.go +++ b/iapi/structs.go @@ -139,3 +139,20 @@ type UserStruct struct { type UserAttrs struct { Email string `json:"email"` } + +//NotificationStruct stores notification results +type NotificationStruct struct { + Attrs NotificationAttrs `json:"attrs"` + Joins struct{} `json:"joins"` + Name string `json:"name"` + Type string `json:"type"` +} + +type NotificationAttrs struct { + Command string `json:"command"` + Users []string `json:"users"` + Servicename string `json:"service_name"` + Interval int `json:"interval"` + Vars interface{} `json:"vars"` + Templates []string `json:"templates"` +}