You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by de...@apache.org on 2018/06/26 18:54:51 UTC
[trafficcontrol] branch master updated (75ac91e -> 0a117a6)
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git.
from 75ac91e Update profile_parameter.rst
new 4c6d7d2 add missing return after error
new 85e13c1 adds db validation
new 23aa135 add tenants structs
new 7b7d75f adds CRUD for tenants
new cd7960f add tenants routes
new ba9f6f0 add tenants API tests
new f2118fc backward compatability
new 1d822da fix tenant tests
new 49fddb2 redo check for deleting tenant with children
new 0c80978 safety check
new d1e9cc7 test deleting tenant
new 35e9f40 detect constraint errors when deleting tenants
new e043ae3 complete tenant test
new c7cf5d0 add last_updated
new 0a117a6 use 1.1 for tenants endpoints
The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
lib/go-tc/tenants.go | 57 +--
lib/go-tc/tovalidate/db.go | 125 +++++++
traffic_ops/client/v13/tenant.go | 17 +
.../api/v13/deliveryservice_requests_test.go | 1 +
traffic_ops/testing/api/v13/tc-fixtures.json | 147 +++++++-
traffic_ops/testing/api/v13/tenants_test.go | 142 +++++++
traffic_ops/testing/api/v13/todb.go | 28 +-
.../traffic_ops_golang/api/shared_handlers.go | 1 +
traffic_ops/traffic_ops_golang/routes.go | 8 +
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 413 +++++++++++++++++++--
10 files changed, 867 insertions(+), 72 deletions(-)
create mode 100644 lib/go-tc/tovalidate/db.go
create mode 100644 traffic_ops/testing/api/v13/tenants_test.go
[trafficcontrol] 12/15: detect constraint errors when deleting
tenants
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 35e9f4040f07b91386d7b518b3e21131ffa0ba08
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Fri Jun 22 11:03:36 2018 -0600
detect constraint errors when deleting tenants
---
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index 9736ba8..05de1c0 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -490,7 +490,29 @@ func (ten *TOTenant) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiEr
result, err := tx.NamedExec(deleteQuery(), ten)
if err != nil {
if pqErr, ok := err.(*pq.Error); ok {
- log.Infof("deleting tenant: %++v", pqErr)
+ err = fmt.Errorf("pqErr is %++v\n", pqErr)
+ var existing string
+ switch pqErr.Table {
+ case "tenant":
+ existing = "child tenants"
+ case "tm_user":
+ existing = "users"
+ case "deliveryservice":
+ existing = "deliveryservices"
+ case "origin":
+ existing = "origins"
+ default:
+ existing = pqErr.Table
+ }
+
+ // another query to get tenant name for the error message
+ name := strconv.Itoa(*ten.ID)
+ if err := db.QueryRow(`SELECT name FROM tenant WHERE id = $1`, *ten.ID).Scan(&name); err != nil {
+ // use ID as a backup for name the error -- this should never happen
+ log.Debugf("error getting tenant name: %++v", err)
+ }
+
+ err = errors.New("Tenant '" + name + "' has " + existing + ". Please update these " + existing + " and retry.")
return err, tc.DataConflictError
}
log.Errorf("received error: %++v from delete execution", err)
[trafficcontrol] 01/15: add missing return after error
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 4c6d7d29b25ebf39f4658a352f4b57dfd8549c3a
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 13:51:00 2018 -0600
add missing return after error
---
traffic_ops/traffic_ops_golang/api/shared_handlers.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
index c6f645c..c1cc8d3 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
@@ -135,6 +135,7 @@ func ReadHandler(typeRef Reader, db *sqlx.DB) http.HandlerFunc {
if err != nil {
log.Errorf("unable to get parameters from request: %s", err)
handleErrs(http.StatusInternalServerError, err)
+ return
}
user, err := auth.GetCurrentUser(ctx)
[trafficcontrol] 09/15: redo check for deleting tenant with children
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 49fddb2cafa76f4bcd09cbd0ab0fc2e785f00d52
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 16:57:55 2018 -0600
redo check for deleting tenant with children
---
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 21 ++++-----------------
1 file changed, 4 insertions(+), 17 deletions(-)
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index 5b706d6..9736ba8 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -486,26 +486,13 @@ func (ten *TOTenant) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiEr
return tc.DBError, tc.SystemError
}
- // if tenant has children, don't allow deletion
- parentQ := `SELECT COUNT(*) FROM tenant WHERE parent_id = $1`
- var count int
- err = tx.QueryRowx(parentQ, *ten.ID).Scan(&count)
- if err != nil {
- log.Errorf("received error: %++v from parent query execution", err)
- return err, tc.SystemError
- }
- if count > 0 {
- name := "unknown"
- if ten.Name != nil {
- name = *ten.Name
- }
- log.Errorf("Tenant '%s' has children tenant(s); refusing to delete", name)
- return fmt.Errorf("Tenant '%s' has children tenant(s): Please update these tenants and retry.", name), tc.ForbiddenError
- }
-
log.Debugf("about to run exec query: %s with tenant: %++v", deleteQuery(), ten)
result, err := tx.NamedExec(deleteQuery(), ten)
if err != nil {
+ if pqErr, ok := err.(*pq.Error); ok {
+ log.Infof("deleting tenant: %++v", pqErr)
+ return err, tc.DataConflictError
+ }
log.Errorf("received error: %++v from delete execution", err)
return tc.DBError, tc.SystemError
}
[trafficcontrol] 08/15: fix tenant tests
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 1d822dab81a79419914d60729cfc4975933a750b
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 15:06:02 2018 -0600
fix tenant tests
---
lib/go-tc/tenants.go | 19 ++++-------
traffic_ops/client/v13/tenant.go | 13 +++++++
traffic_ops/testing/api/v13/tenants_test.go | 47 +++++++++++---------------
traffic_ops/testing/api/v13/traffic_control.go | 2 +-
4 files changed, 40 insertions(+), 41 deletions(-)
diff --git a/lib/go-tc/tenants.go b/lib/go-tc/tenants.go
index 2448822..e825945 100644
--- a/lib/go-tc/tenants.go
+++ b/lib/go-tc/tenants.go
@@ -19,24 +19,17 @@ package tc
* under the License.
*/
-// TenantResponse is the response for a request for a single tenant
-type TenantResponse struct {
- Response Tenant `json:"response"`
- Alerts []TenantAlert `json:"alerts"`
+// GetTenantsResponse is the response for a request for a group of tenants
+type GetTenantsResponse struct {
+ Response []Tenant `json:"response"`
}
-// GetTenantResponse here to maintain backward compatability
-type GetTenantResponse TenantResponse
-
-// TenantResponse is the response for a request for a group of tenants
-type TenantsResponse struct {
- Response []Tenant `json:"response"`
+// TenantResponse is the response to a create/update
+type TenantResponse struct {
+ Response Tenant `json:"response"`
Alerts []TenantAlert `json:"alerts"`
}
-// GetTenantsResponse here to maintain backward compatability
-type GetTenantsResponse TenantsResponse
-
// Tenant ...
type Tenant struct {
Active bool `json:"active"`
diff --git a/traffic_ops/client/v13/tenant.go b/traffic_ops/client/v13/tenant.go
index 123828e..27bd31a 100644
--- a/traffic_ops/client/v13/tenant.go
+++ b/traffic_ops/client/v13/tenant.go
@@ -17,6 +17,7 @@ package v13
import (
"encoding/json"
+ "net/url"
tc "github.com/apache/trafficcontrol/lib/go-tc"
)
@@ -43,6 +44,18 @@ func (to *Session) Tenant(id string) (*tc.Tenant, ReqInf, error) {
return &data.Response[0], reqInf, nil
}
+// TenantByName gets the Tenant for the name it's passed
+func (to *Session) TenantByName(name string) (*tc.Tenant, ReqInf, error) {
+ var data tc.GetTenantsResponse
+ query := tenantsEp() + "?name=" + url.QueryEscape(name)
+ reqInf, err := get(to, query, &data)
+ if err != nil {
+ return nil, reqInf, err
+ }
+
+ return &data.Response[0], reqInf, nil
+}
+
// CreateTenant creates the Tenant it's passed
func (to *Session) CreateTenant(t *tc.Tenant) (*tc.TenantResponse, error) {
var data tc.TenantResponse
diff --git a/traffic_ops/testing/api/v13/tenants_test.go b/traffic_ops/testing/api/v13/tenants_test.go
index feb205c..519b088 100644
--- a/traffic_ops/testing/api/v13/tenants_test.go
+++ b/traffic_ops/testing/api/v13/tenants_test.go
@@ -16,9 +16,8 @@ package v13
*/
import (
+ "strconv"
"testing"
-
- tc "github.com/apache/trafficcontrol/lib/go-tc"
)
func TestTenants(t *testing.T) {
@@ -33,14 +32,14 @@ func CreateTestTenants(t *testing.T) {
for _, ten := range testData.Tenants {
// testData does not define ParentID -- look up by name and fill in
if ten.ParentID == 0 {
- parents, _, err := TOSession.GetTenantByName(ten.ParentName)
+ parent, _, err := TOSession.TenantByName(ten.ParentName)
if err != nil {
t.Errorf("parent tenant %s: %++v", ten.ParentName, err)
continue
}
- ten.ParentID = parents[0].ID
+ ten.ParentID = parent.ID
}
- resp, _, err := TOSession.CreateTenant(ten)
+ resp, err := TOSession.CreateTenant(&ten)
t.Logf("response: %++v", resp)
if err != nil {
@@ -50,7 +49,7 @@ func CreateTestTenants(t *testing.T) {
}
func GetTestTenants(t *testing.T) {
- resp, _, err := TOSession.GetTenants()
+ resp, _, err := TOSession.Tenants()
if err != nil {
t.Errorf("cannot GET all tenants: %v - %v\n", err, resp)
return
@@ -62,13 +61,13 @@ func GetTestTenants(t *testing.T) {
}
for _, ten := range testData.Tenants {
- resp, _, err := TOSession.GetTenantByName(ten.Name)
+ resp, _, err := TOSession.TenantByName(ten.Name)
if err != nil {
t.Errorf("cannot GET Tenant by name: %v - %v\n", err, resp)
continue
}
- if len(resp) != 1 {
- t.Errorf("expected 1 tenant for %s, got %d", ten.Name, len(resp))
+ if resp.Name != ten.Name {
+ t.Errorf("expected tenant %s, got %s", ten.Name, resp.Name)
continue
}
}
@@ -79,30 +78,28 @@ func UpdateTestTenants(t *testing.T) {
// Retrieve the Tenant by name so we can get the id for the Update
name := "tenant2"
parentName := "tenant1"
- resp, _, err := TOSession.GetTenantByName(name)
+ modTenant, _, err := TOSession.TenantByName(name)
if err != nil {
t.Errorf("cannot GET Tenant by name: %s - %v\n", name, err)
}
- modTenant := resp[0]
- resp, _, err = TOSession.GetTenantByName(parentName)
+
+ newParent, _, err := TOSession.TenantByName(parentName)
if err != nil {
- t.Errorf("cannot GET Tenant by name: %s - %v\n", name, err)
+ t.Errorf("cannot GET Tenant by name: %s - %v\n", parentName, err)
}
- newParent := resp[0]
modTenant.ParentID = newParent.ID
- var alert tc.Alerts
- alert, _, err = TOSession.UpdateTenantByID(modTenant.ID, modTenant)
+
+ resp, err := TOSession.UpdateTenant(strconv.Itoa(modTenant.ID), modTenant)
if err != nil {
- t.Errorf("cannot UPDATE Tenant by id: %v - %v\n", err, alert)
+ t.Errorf("cannot UPDATE Tenant by id: %v\n", err)
}
// Retrieve the Tenant to check Tenant parent name got updated
- resp, _, err = TOSession.GetTenantByID(modTenant.ID)
+ respTenant, _, err := TOSession.Tenant(strconv.Itoa(modTenant.ID))
if err != nil {
t.Errorf("cannot GET Tenant by name: %v - %v\n", name, err)
}
t.Logf("modified: %++v", resp)
- respTenant := resp[0]
if respTenant.ParentName != parentName {
t.Errorf("results do not match actual: %s, expected: %s\n", respTenant.ParentName, parentName)
}
@@ -113,24 +110,20 @@ func DeleteTestTenants(t *testing.T) {
for _, ten := range testData.Tenants {
// Retrieve the Tenant by name so we can get the id for the Update
- resp, _, err := TOSession.GetTenantByName(ten.Name)
+ respTenant, _, err := TOSession.TenantByName(ten.Name)
if err != nil {
t.Errorf("cannot GET Tenant by name: %v - %v\n", ten.Name, err)
}
- respTenant := resp[0]
- delResp, _, err := TOSession.DeleteTenantByID(respTenant.ID)
+ delResp, err := TOSession.DeleteTenant(strconv.Itoa(respTenant.ID))
if err != nil {
t.Errorf("cannot DELETE Tenant by name: %v - %v\n", err, delResp)
}
// Retrieve the Tenant to see if it got deleted
- Tenants, _, err := TOSession.GetTenantByName(ten.Name)
+ _, _, err = TOSession.TenantByName(ten.Name)
if err != nil {
- t.Errorf("error deleting Tenant name: %s\n", err.Error())
- }
- if len(Tenants) > 0 {
- t.Errorf("expected Tenant name: %s to be deleted\n", ten.Name)
+ t.Errorf("tenant not deleted: %v\n", err)
}
}
}
diff --git a/traffic_ops/testing/api/v13/traffic_control.go b/traffic_ops/testing/api/v13/traffic_control.go
index bbe7d13..98fcdfc 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -39,6 +39,6 @@ type TrafficControl struct {
Roles []v13.Role `json:"roles"`
Servers []v13.Server `json:"servers"`
Statuses []v12.Status `json:"statuses"`
- Tenants []v13.Tenant `json:"tenants"`
+ Tenants []v12.Tenant `json:"tenants"`
Types []v12.Type `json:"types"`
}
[trafficcontrol] 07/15: backward compatability
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit f2118fce50d7844959f89c84dd63d5b89f1be672
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 14:33:19 2018 -0600
backward compatability
---
lib/go-tc/tenants.go | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/lib/go-tc/tenants.go b/lib/go-tc/tenants.go
index c53e707..2448822 100644
--- a/lib/go-tc/tenants.go
+++ b/lib/go-tc/tenants.go
@@ -19,18 +19,24 @@ package tc
* under the License.
*/
-// TenantResponse ...
+// TenantResponse is the response for a request for a single tenant
type TenantResponse struct {
Response Tenant `json:"response"`
Alerts []TenantAlert `json:"alerts"`
}
-// TenantResponse ...
+// GetTenantResponse here to maintain backward compatability
+type GetTenantResponse TenantResponse
+
+// TenantResponse is the response for a request for a group of tenants
type TenantsResponse struct {
Response []Tenant `json:"response"`
Alerts []TenantAlert `json:"alerts"`
}
+// GetTenantsResponse here to maintain backward compatability
+type GetTenantsResponse TenantsResponse
+
// Tenant ...
type Tenant struct {
Active bool `json:"active"`
[trafficcontrol] 02/15: adds db validation
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 85e13c1a32bf439e5b7c9b1a40da4e70c1a3d097
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 14:19:12 2018 -0600
adds db validation
---
lib/go-tc/tovalidate/db.go | 125 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 125 insertions(+)
diff --git a/lib/go-tc/tovalidate/db.go b/lib/go-tc/tovalidate/db.go
new file mode 100644
index 0000000..dd14b9b
--- /dev/null
+++ b/lib/go-tc/tovalidate/db.go
@@ -0,0 +1,125 @@
+package tovalidate
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ validation "github.com/go-ozzo/ozzo-validation"
+ "github.com/jmoiron/sqlx"
+)
+
+// DBExistsRule checks if value is in column in this table
+// TODO: DBExistsRule ? what about DBUniqueRule?
+type DBExistsRule struct {
+ db *sqlx.DB
+ table string
+ column string
+ message string
+}
+
+// NewDBExistsRule is a validation rule that checks if the given value is in the column in this table
+func NewDBExistsRule(db *sqlx.DB, table string, column string) *DBExistsRule {
+ return &DBExistsRule{
+ db: db,
+ table: table,
+ column: column,
+ message: fmt.Sprintf("No rows with value in %s.%s", table, column),
+ }
+}
+
+// Validate checks if the given value is valid or not.
+func (r *DBExistsRule) Validate(value interface{}) error {
+ if r.db == nil {
+ return nil
+ }
+ value, isNil := validation.Indirect(value)
+ if isNil || validation.IsEmpty(value) {
+ return nil
+ }
+
+ query := `SELECT COUNT(*) FROM ` + r.table + ` WHERE ` + r.column + `= $1`
+ row := r.db.QueryRow(query, value)
+ var cnt int
+ err := row.Scan(&cnt)
+ log.Debugln("**** QUERY **** ", query)
+ log.Debugf(" value %d err %++v", cnt, err)
+ if err != nil {
+ return errors.New(r.message)
+ }
+ return nil
+}
+
+// Error sets the error message for the rule.
+func (r *DBExistsRule) Error(message string) *DBExistsRule {
+ r.message = message
+ return r
+}
+
+// DBUniqueRule checks if value is in column in this table
+// TODO: DBUniqueRule ? what about DBUniqueRule?
+type DBUniqueRule struct {
+ db *sqlx.DB
+ table string
+ column string
+ idCheck func(int) bool
+ message string
+}
+
+// NewDBUniqueRule is a validation rule that checks if the given value is in the column in this table
+func NewDBUniqueRule(db *sqlx.DB, table string, column string, idCheck func(int) bool) *DBUniqueRule {
+ return &DBUniqueRule{
+ db: db,
+ table: table,
+ column: column,
+ idCheck: idCheck,
+ message: column + ` must be unique in ` + table,
+ }
+}
+
+// Validate returns an error if the value already exists in the table in this column
+func (r *DBUniqueRule) Validate(value interface{}) error {
+ if r.db == nil {
+ return nil
+ }
+ value, isNil := validation.Indirect(value)
+ if isNil || validation.IsEmpty(value) {
+ return nil
+ }
+
+ query := `SELECT id FROM ` + r.table
+ row := r.db.QueryRowx(query, map[string]interface{}{r.column: value})
+ var id int
+ err := row.Scan(&id)
+ // ok if no rows found or only one belongs to row being updated
+ if err == sql.ErrNoRows || r.idCheck(id) {
+ return nil
+ }
+ return errors.New(r.message)
+}
+
+// Error sets the error message for the rule.
+func (r *DBUniqueRule) Error(message string) *DBUniqueRule {
+ r.message = message
+ return r
+}
[trafficcontrol] 11/15: test deleting tenant
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit d1e9cc7c500586b3008f92eedd28e23f70d09419
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 16:58:55 2018 -0600
test deleting tenant
---
traffic_ops/testing/api/v13/tenants_test.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/traffic_ops/testing/api/v13/tenants_test.go b/traffic_ops/testing/api/v13/tenants_test.go
index 519b088..dc08086 100644
--- a/traffic_ops/testing/api/v13/tenants_test.go
+++ b/traffic_ops/testing/api/v13/tenants_test.go
@@ -25,7 +25,7 @@ func TestTenants(t *testing.T) {
CreateTestTenants(t)
UpdateTestTenants(t)
GetTestTenants(t)
- //DeleteTestTenants(t)
+ DeleteTestTenants(t)
}
func CreateTestTenants(t *testing.T) {
@@ -117,7 +117,7 @@ func DeleteTestTenants(t *testing.T) {
delResp, err := TOSession.DeleteTenant(strconv.Itoa(respTenant.ID))
if err != nil {
- t.Errorf("cannot DELETE Tenant by name: %v - %v\n", err, delResp)
+ t.Errorf("cannot DELETE Tenant: %v - %v\n", err, delResp)
}
// Retrieve the Tenant to see if it got deleted
[trafficcontrol] 10/15: safety check
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 0c809780c9c97185fe81773c7e8ca91aa65c5755
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 16:58:32 2018 -0600
safety check
---
traffic_ops/client/v13/tenant.go | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/traffic_ops/client/v13/tenant.go b/traffic_ops/client/v13/tenant.go
index 27bd31a..ac9bd4c 100644
--- a/traffic_ops/client/v13/tenant.go
+++ b/traffic_ops/client/v13/tenant.go
@@ -53,7 +53,11 @@ func (to *Session) TenantByName(name string) (*tc.Tenant, ReqInf, error) {
return nil, reqInf, err
}
- return &data.Response[0], reqInf, nil
+ var ten *tc.Tenant
+ if len(data.Response) > 0 {
+ ten = &data.Response[0]
+ }
+ return ten, reqInf, nil
}
// CreateTenant creates the Tenant it's passed
[trafficcontrol] 13/15: complete tenant test
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit e043ae3b18e35dca7c6198135d3714162b7e212f
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Fri Jun 22 13:45:34 2018 -0600
complete tenant test
---
traffic_ops/testing/api/v13/tenants_test.go | 43 +++++++++++++++++++----------
1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/traffic_ops/testing/api/v13/tenants_test.go b/traffic_ops/testing/api/v13/tenants_test.go
index dc08086..99bbbf2 100644
--- a/traffic_ops/testing/api/v13/tenants_test.go
+++ b/traffic_ops/testing/api/v13/tenants_test.go
@@ -17,6 +17,7 @@ package v13
import (
"strconv"
+ "strings"
"testing"
)
@@ -108,22 +109,34 @@ func UpdateTestTenants(t *testing.T) {
func DeleteTestTenants(t *testing.T) {
- for _, ten := range testData.Tenants {
- // Retrieve the Tenant by name so we can get the id for the Update
- respTenant, _, err := TOSession.TenantByName(ten.Name)
- if err != nil {
- t.Errorf("cannot GET Tenant by name: %v - %v\n", ten.Name, err)
- }
+ t1 := "tenant1"
+ tenant1, _, err := TOSession.TenantByName(t1)
- delResp, err := TOSession.DeleteTenant(strconv.Itoa(respTenant.ID))
- if err != nil {
- t.Errorf("cannot DELETE Tenant: %v - %v\n", err, delResp)
- }
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %v - %v\n", t1, err)
+ }
- // Retrieve the Tenant to see if it got deleted
- _, _, err = TOSession.TenantByName(ten.Name)
- if err != nil {
- t.Errorf("tenant not deleted: %v\n", err)
- }
+ _, err = TOSession.DeleteTenant(strconv.Itoa(tenant1.ID))
+ if err == nil {
+ t.Errorf("%s has child tenants -- should not be able to delete", t1)
}
+ expected := "Tenant 'tenant1' has child tenants"
+ if !strings.Contains(err.Error(), expected) {
+ t.Errorf("expected error: %s; got %s", expected, err.Error())
+ }
+
+ t2 := "tenant2"
+ tenant2, _, err := TOSession.TenantByName(t2)
+ _, err = TOSession.DeleteTenant(strconv.Itoa(tenant2.ID))
+ if err != nil {
+ t.Errorf("error deleting tenant %s: %v", t2, err)
+ }
+
+ // Now should be able to delete t1
+ tenant1, _, err = TOSession.TenantByName(t1)
+ _, err = TOSession.DeleteTenant(strconv.Itoa(tenant1.ID))
+ if err != nil {
+ t.Errorf("error deleting tenant %s: %v", t1, err)
+ }
+
}
[trafficcontrol] 14/15: add last_updated
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit c7cf5d042c86b2987b6c51f57172e37ebeb651b1
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Fri Jun 22 13:59:26 2018 -0600
add last_updated
---
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index 05de1c0..44f1363 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -542,6 +542,7 @@ func selectQuery() string {
t.active AS active,
t.name AS name,
t.id AS id,
+t.last_updated AS last_updated,
t.parent_id AS parent_id,
p.name AS parent_name
[trafficcontrol] 04/15: adds CRUD for tenants
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 7b7d75f534ad2e3cd8ee2244a6fc197e67c831f8
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 14:04:29 2018 -0600
adds CRUD for tenants
---
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 403 +++++++++++++++++++++--
1 file changed, 373 insertions(+), 30 deletions(-)
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index 177a32d..5b706d6 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -24,25 +24,27 @@ import (
"errors"
"fmt"
"net/http"
+ "strconv"
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
-
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+ validation "github.com/go-ozzo/ozzo-validation"
"github.com/jmoiron/sqlx"
+ "github.com/lib/pq"
)
-type Tenant struct {
- ID int
- Name string
- Active bool
- ParentID int
-}
+// TOTenant provides a local type against which to define methods
+type TOTenant tc.TenantNullable
+// DeliveryServiceTenantInfo provides only deliveryservice info needed here
type DeliveryServiceTenantInfo tc.DeliveryServiceNullable
-// returns true if the user has tenant access on this deliveryservice
+// IsTenantAuthorized returns true if the user has tenant access on this tenant
func (dsInfo DeliveryServiceTenantInfo) IsTenantAuthorized(user *auth.CurrentUser, tx *sql.Tx) (bool, error) {
if dsInfo.TenantID == nil {
return false, errors.New("TenantID is nil")
@@ -62,15 +64,16 @@ func GetDeliveryServiceTenantInfo(xmlID string, tx *sql.Tx) (*DeliveryServiceTen
return &ds, nil
}
-// Check checks that the given user has access to the given XMLID. Returns a user error, system error, and the HTTP status code to be returned to the user if an error occurred. On success, the user error and system error will both be nil, and the error code should be ignored.
+// Check checks that the given user has access to the given XMLID. Returns a user error, system error,
+// and the HTTP status code to be returned to the user if an error occurred. On success, the user error
+// and system error will both be nil, and the error code should be ignored.
func Check(user *auth.CurrentUser, XMLID string, tx *sql.Tx) (error, error, int) {
dsInfo, err := GetDeliveryServiceTenantInfo(XMLID, tx)
if err != nil {
if dsInfo == nil {
return nil, errors.New("deliveryservice lookup failure: " + err.Error()), http.StatusInternalServerError
- } else {
- return errors.New("no such deliveryservice: '" + XMLID + "'"), nil, http.StatusBadRequest
}
+ return errors.New("no such deliveryservice: '" + XMLID + "'"), nil, http.StatusBadRequest
}
hasAccess, err := dsInfo.IsTenantAuthorized(user, tx)
if err != nil {
@@ -86,35 +89,33 @@ func Check(user *auth.CurrentUser, XMLID string, tx *sql.Tx) (error, error, int)
// NOTE: This method does not use the use_tenancy parameter and if this method is being used
// to control tenancy the parameter must be checked. The method IsResourceAuthorizedToUser checks the use_tenancy parameter
// and should be used for this purpose in most cases.
-func GetUserTenantList(user auth.CurrentUser, db *sqlx.DB) ([]Tenant, error) {
- query := `WITH RECURSIVE q AS (SELECT id, name, active, parent_id FROM tenant WHERE id = $1
- UNION SELECT t.id, t.name, t.active, t.parent_id FROM tenant t JOIN q ON q.id = t.parent_id)
- SELECT id, name, active, parent_id FROM q;`
+func GetUserTenantList(user auth.CurrentUser, db *sqlx.DB) ([]TOTenant, error) {
+ query := `WITH RECURSIVE q AS (SELECT id, name, active, parent_id, last_updated FROM tenant WHERE id = $1
+ UNION SELECT t.id, t.name, t.active, t.parent_id, t.last_updated FROM tenant t JOIN q ON q.id = t.parent_id)
+ SELECT id, name, active, parent_id, last_updated FROM q;`
log.Debugln("\nQuery: ", query)
- var tenantID int
- var name string
- var active bool
- var parentID *int
-
+ var (
+ tenantID, parentID int
+ name string
+ active bool
+ lastUpdated tc.TimeNoMod
+ )
rows, err := db.Query(query, user.TenantID)
if err != nil {
return nil, err
}
defer rows.Close()
- tenants := []Tenant{}
+ tenants := []TOTenant{}
for rows.Next() {
- if err := rows.Scan(&tenantID, &name, &active, &parentID); err != nil {
+ if err := rows.Scan(&tenantID, &name, &active, &parentID, &lastUpdated); err != nil {
return nil, err
}
- if parentID != nil {
- tenants = append(tenants, Tenant{ID: tenantID, Name: name, Active: active, ParentID: *parentID})
- } else {
- tenants = append(tenants, Tenant{ID: tenantID, Name: name, Active: active, ParentID: -1})
- }
+
+ tenants = append(tenants, TOTenant{ID: &tenantID, Name: &name, Active: &active, ParentID: &parentID})
}
return tenants, nil
@@ -189,10 +190,8 @@ func IsResourceAuthorizedToUser(resourceTenantID int, user auth.CurrentUser, db
}
if active && tenantID == resourceTenantID {
return true, nil
- } else {
- fmt.Printf("default")
- return false, nil
}
+ return false, nil
}
}
@@ -228,3 +227,347 @@ func IsResourceAuthorizedToUserTx(resourceTenantID int, user *auth.CurrentUser,
}
}
}
+
+// GetRefType allows shared handlers to decode JSON to the tenant type
+// Part of the Identifier interface
+func GetRefType() *TOTenant {
+ return &TOTenant{}
+}
+
+// GetID wraps the ID member with null checking
+// Part of the Identifier interface
+func (ten TOTenant) GetID() (int, bool) {
+ if ten.ID == nil {
+ return 0, false
+ }
+ return *ten.ID, true
+}
+
+// GetKeyFieldsInfo identifies types of the key fields
+func (ten TOTenant) GetKeyFieldsInfo() []api.KeyFieldInfo {
+ return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
+// GetKeys returns values of keys
+func (ten TOTenant) GetKeys() (map[string]interface{}, bool) {
+ var id int
+ if ten.ID != nil {
+ id = *ten.ID
+ }
+ return map[string]interface{}{"id": id}, true
+}
+
+// GetAuditName returns a unique identifier
+// Part of the Identifier interface
+func (ten TOTenant) GetAuditName() string {
+ if ten.Name != nil {
+ return *ten.Name
+ }
+ id, _ := ten.GetID()
+ return strconv.Itoa(id)
+}
+
+// GetType returns the name of the type for messages
+// Part of the Identifier interface
+func (ten TOTenant) GetType() string {
+ return "tenant"
+}
+
+// SetKeys allows CreateHandler to assign id once object is created.
+// Part of the Identifier interface
+func (ten *TOTenant) SetKeys(keys map[string]interface{}) {
+ i, _ := keys["id"].(int) //this utilizes the non panicking type assertion, if the thrown away ok variable is false i will be the zero of the type, 0 here.
+ ten.ID = &i
+}
+
+// Validate fulfills the api.Validator interface
+func (ten TOTenant) Validate(db *sqlx.DB) []error {
+ errs := validation.Errors{
+ "name": validation.Validate(ten.Name, validation.Required),
+ "active": validation.Validate(ten.Active), // only validate it's boolean
+ "parentId": validation.Validate(ten.ParentID, validation.Required, validation.Min(1)),
+ }
+ return tovalidate.ToErrors(errs)
+}
+
+// Create implements the Creator interface
+//all implementations of Creator should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a tenant with conflicting values exists
+//if so, it will return an errorType of DataConflict and the type should be appended to the
+//generic error message returned
+//The insert sql returns the id and lastUpdated values of the newly inserted tenant and have
+//to be added to the struct
+func (ten *TOTenant) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+ rollbackTransaction := true
+ tx, err := db.Beginx()
+ defer func() {
+ if tx == nil || !rollbackTransaction {
+ return
+ }
+ err := tx.Rollback()
+ if err != nil {
+ log.Errorln(errors.New("rolling back transaction: " + err.Error()))
+ }
+ }()
+
+ if err != nil {
+ log.Error.Printf("could not begin transaction: %v", err)
+ return tc.DBError, tc.SystemError
+ }
+ resultRows, err := tx.NamedQuery(insertQuery(), ten)
+ if err != nil {
+ if pqErr, ok := err.(*pq.Error); ok {
+ err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+ if eType == tc.DataConflictError {
+ return errors.New("a tenant with " + err.Error()), eType
+ }
+ return err, eType
+ }
+ log.Errorf("received non pq error: %++v from create execution", err)
+ return tc.DBError, tc.SystemError
+ }
+ defer resultRows.Close()
+
+ var id int
+ var lastUpdated tc.TimeNoMod
+ rowsAffected := 0
+ for resultRows.Next() {
+ rowsAffected++
+ if err := resultRows.Scan(&id, &lastUpdated); err != nil {
+ log.Error.Printf("could not scan id from insert: %s\n", err)
+ return tc.DBError, tc.SystemError
+ }
+ }
+ if rowsAffected == 0 {
+ err = errors.New("no tenant was inserted, no id was returned")
+ log.Errorln(err)
+ return tc.DBError, tc.SystemError
+ } else if rowsAffected > 1 {
+ err = errors.New("too many ids returned from tenant insert")
+ log.Errorln(err)
+ return tc.DBError, tc.SystemError
+ }
+ ten.SetKeys(map[string]interface{}{"id": id})
+ ten.LastUpdated = &lastUpdated
+ err = tx.Commit()
+ if err != nil {
+ log.Errorln("Could not commit transaction: ", err)
+ return tc.DBError, tc.SystemError
+ }
+ rollbackTransaction = false
+ return nil, tc.NoError
+}
+
+// Read implements the tc.Reader interface
+func (ten *TOTenant) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+ var rows *sqlx.Rows
+
+ // Query Parameters to Database Query column mappings
+ // see the fields mapped in the SQL query
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "active": dbhelpers.WhereColumnInfo{Column: "t.active", Checker: nil},
+ "id": dbhelpers.WhereColumnInfo{Column: "t.id", Checker: api.IsInt},
+ "name": dbhelpers.WhereColumnInfo{Column: "t.name", Checker: nil},
+ "parent_id": dbhelpers.WhereColumnInfo{Column: "t.parentID", Checker: api.IsInt},
+ "parent_name": dbhelpers.WhereColumnInfo{Column: "p.name", Checker: api.IsInt},
+ }
+ where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, errs, tc.DataConflictError
+ }
+
+ query := selectQuery() + where + orderBy
+ log.Debugln("Query is ", query)
+
+ rows, err := db.NamedQuery(query, queryValues)
+ if err != nil {
+ log.Errorf("Error querying tenants: %v", err)
+ return nil, []error{tc.DBError}, tc.SystemError
+ }
+ defer rows.Close()
+
+ tenants := []interface{}{}
+ for rows.Next() {
+ var s TOTenant
+ if err = rows.StructScan(&s); err != nil {
+ log.Errorf("error parsing Tenant rows: %v", err)
+ return nil, []error{tc.DBError}, tc.SystemError
+ }
+ tenants = append(tenants, s)
+ }
+
+ return tenants, []error{}, tc.NoError
+}
+
+//The TOTenant implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a tenant with conflicting values exists
+//if so, it will return an errorType of DataConflict and the type should be appended to the
+//generic error message returned
+func (ten *TOTenant) Update(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+ rollbackTransaction := true
+ tx, err := db.Beginx()
+ defer func() {
+ if tx == nil || !rollbackTransaction {
+ return
+ }
+ err := tx.Rollback()
+ if err != nil {
+ log.Errorln(errors.New("rolling back transaction: " + err.Error()))
+ }
+ }()
+
+ if err != nil {
+ log.Error.Printf("could not begin transaction: %v", err)
+ return tc.DBError, tc.SystemError
+ }
+ log.Debugf("about to run exec query: %s with tenant: %++v", updateQuery(), ten)
+ resultRows, err := tx.NamedQuery(updateQuery(), ten)
+ if err != nil {
+ if pqErr, ok := err.(*pq.Error); ok {
+ err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+ if eType == tc.DataConflictError {
+ return errors.New("a tenant with " + err.Error()), eType
+ }
+ return err, eType
+ }
+ log.Errorf("received error: %++v from update execution", err)
+ return tc.DBError, tc.SystemError
+ }
+ defer resultRows.Close()
+
+ var lastUpdated tc.TimeNoMod
+ rowsAffected := 0
+ for resultRows.Next() {
+ rowsAffected++
+ if err := resultRows.Scan(&lastUpdated); err != nil {
+ log.Error.Printf("could not scan lastUpdated from insert: %s\n", err)
+ return tc.DBError, tc.SystemError
+ }
+ }
+ log.Debugf("lastUpdated: %++v", lastUpdated)
+ ten.LastUpdated = &lastUpdated
+ if rowsAffected != 1 {
+ if rowsAffected < 1 {
+ return errors.New("no tenant found with this id"), tc.DataMissingError
+ }
+ return fmt.Errorf("this update affected too many rows: %d", rowsAffected), tc.SystemError
+ }
+ err = tx.Commit()
+ if err != nil {
+ log.Errorln("Could not commit transaction: ", err)
+ return tc.DBError, tc.SystemError
+ }
+ rollbackTransaction = false
+ return nil, tc.NoError
+}
+
+//Delete implements the Deleter interface
+//all implementations of Deleter should use transactions and return the proper errorType
+func (ten *TOTenant) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+ if ten.ID == nil {
+ // should never happen...
+ return errors.New("invalid tenant: id is nil"), tc.SystemError
+ }
+ rollbackTransaction := true
+ tx, err := db.Beginx()
+ defer func() {
+ if tx == nil || !rollbackTransaction {
+ return
+ }
+ err := tx.Rollback()
+ if err != nil {
+ log.Errorln(errors.New("rolling back transaction: " + err.Error()))
+ }
+ }()
+
+ if err != nil {
+ log.Error.Printf("could not begin transaction: %v", err)
+ return tc.DBError, tc.SystemError
+ }
+
+ // if tenant has children, don't allow deletion
+ parentQ := `SELECT COUNT(*) FROM tenant WHERE parent_id = $1`
+ var count int
+ err = tx.QueryRowx(parentQ, *ten.ID).Scan(&count)
+ if err != nil {
+ log.Errorf("received error: %++v from parent query execution", err)
+ return err, tc.SystemError
+ }
+ if count > 0 {
+ name := "unknown"
+ if ten.Name != nil {
+ name = *ten.Name
+ }
+ log.Errorf("Tenant '%s' has children tenant(s); refusing to delete", name)
+ return fmt.Errorf("Tenant '%s' has children tenant(s): Please update these tenants and retry.", name), tc.ForbiddenError
+ }
+
+ log.Debugf("about to run exec query: %s with tenant: %++v", deleteQuery(), ten)
+ result, err := tx.NamedExec(deleteQuery(), ten)
+ if err != nil {
+ log.Errorf("received error: %++v from delete execution", err)
+ return tc.DBError, tc.SystemError
+ }
+ rowsAffected, err := result.RowsAffected()
+ if err != nil {
+ return tc.DBError, tc.SystemError
+ }
+ if rowsAffected != 1 {
+ if rowsAffected < 1 {
+ return errors.New("no tenant with that id found"), tc.DataMissingError
+ }
+ return fmt.Errorf("this delete affected too many rows: %d", rowsAffected), tc.SystemError
+ }
+ err = tx.Commit()
+ if err != nil {
+ log.Errorln("Could not commit transaction: ", err)
+ return tc.DBError, tc.SystemError
+ }
+ rollbackTransaction = false
+ return nil, tc.NoError
+}
+
+func selectQuery() string {
+ query := `SELECT
+t.active AS active,
+t.name AS name,
+t.id AS id,
+t.parent_id AS parent_id,
+p.name AS parent_name
+
+FROM tenant AS t
+LEFT OUTER JOIN tenant AS p
+ON t.parent_id = p.id`
+ return query
+}
+
+func updateQuery() string {
+ query := `UPDATE
+tenant SET
+active=:active,
+name=:name,
+parent_id=:parent_id
+
+WHERE id=:id RETURNING last_updated`
+ return query
+}
+
+func insertQuery() string {
+ query := `INSERT INTO tenant (
+name,
+active,
+parent_id
+) VALUES (
+:name,
+:active,
+:parent_id
+) RETURNING id,last_updated`
+ return query
+}
+
+func deleteQuery() string {
+ query := `DELETE FROM tenant
+WHERE id=:id`
+ return query
+}
[trafficcontrol] 15/15: use 1.1 for tenants endpoints
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 0a117a649dedc2982e18e71dbd167ce31ec953ec
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Fri Jun 22 14:48:30 2018 -0600
use 1.1 for tenants endpoints
---
traffic_ops/traffic_ops_golang/routes.go | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 13aed57..789a2ec 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -282,11 +282,11 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
{1.1, http.MethodDelete, `profileparameters/{profileId}/{parameterId}$`, api.DeleteHandler(profileparameter.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
//Tenants
- {1.2, http.MethodGet, `tenants/?(\.json)?$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
- {1.2, http.MethodGet, `tenants/{id}$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
- {1.2, http.MethodPut, `tenants/{id}$`, api.UpdateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
- {1.2, http.MethodPost, `tenants/?$`, api.CreateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
- {1.2, http.MethodDelete, `tenants/{id}$`, api.DeleteHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet, `tenants/?(\.json)?$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.1, http.MethodGet, `tenants/{id}$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.1, http.MethodPut, `tenants/{id}$`, api.UpdateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodPost, `tenants/?$`, api.CreateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodDelete, `tenants/{id}$`, api.DeleteHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
//CRConfig
{1.1, http.MethodGet, `cdns/{cdn}/snapshot/?$`, crconfig.SnapshotGetHandler(d.DB, d.Config), crconfig.PrivLevel, Authenticated, nil},
[trafficcontrol] 05/15: add tenants routes
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit cd7960f656b4ce5f9cb3f36b6506f9384ee2e7e4
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 13:57:39 2018 -0600
add tenants routes
---
traffic_ops/traffic_ops_golang/routes.go | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 01cdb92..13aed57 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -57,6 +57,7 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/staticdnsentry"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/status"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/systeminfo"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/types"
"github.com/basho/riak-go-client"
@@ -280,6 +281,13 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
{1.1, http.MethodPost, `parameterprofile/?$`, profileparameter.PostParamProfile(d.DB.DB), auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodDelete, `profileparameters/{profileId}/{parameterId}$`, api.DeleteHandler(profileparameter.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ //Tenants
+ {1.2, http.MethodGet, `tenants/?(\.json)?$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.2, http.MethodGet, `tenants/{id}$`, api.ReadHandler(tenant.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.2, http.MethodPut, `tenants/{id}$`, api.UpdateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ {1.2, http.MethodPost, `tenants/?$`, api.CreateHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+ {1.2, http.MethodDelete, `tenants/{id}$`, api.DeleteHandler(tenant.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+
//CRConfig
{1.1, http.MethodGet, `cdns/{cdn}/snapshot/?$`, crconfig.SnapshotGetHandler(d.DB, d.Config), crconfig.PrivLevel, Authenticated, nil},
{1.1, http.MethodGet, `cdns/{cdn}/snapshot/new/?$`, crconfig.Handler(d.DB, d.Config), crconfig.PrivLevel, Authenticated, nil},
[trafficcontrol] 06/15: add tenants API tests
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit ba9f6f09e39ea9d568be798074bede3d1f15ede2
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 14:12:18 2018 -0600
add tenants API tests
---
.../api/v13/deliveryservice_requests_test.go | 1 +
traffic_ops/testing/api/v13/tc-fixtures.json | 147 ++++++++++++++++++++-
traffic_ops/testing/api/v13/tenants_test.go | 136 +++++++++++++++++++
traffic_ops/testing/api/v13/todb.go | 28 ++--
traffic_ops/testing/api/v13/traffic_control.go | 2 +-
5 files changed, 292 insertions(+), 22 deletions(-)
diff --git a/traffic_ops/testing/api/v13/deliveryservice_requests_test.go b/traffic_ops/testing/api/v13/deliveryservice_requests_test.go
index 6d974ae..1b6589a 100644
--- a/traffic_ops/testing/api/v13/deliveryservice_requests_test.go
+++ b/traffic_ops/testing/api/v13/deliveryservice_requests_test.go
@@ -39,6 +39,7 @@ func TestDeliveryServiceRequests(t *testing.T) {
GetTestDeliveryServiceRequests(t)
UpdateTestDeliveryServiceRequests(t)
DeleteTestDeliveryServiceRequests(t)
+
DeleteTestTypes(t)
DeleteTestCDNs(t)
diff --git a/traffic_ops/testing/api/v13/tc-fixtures.json b/traffic_ops/testing/api/v13/tc-fixtures.json
index c41eb8b..21f61f9 100644
--- a/traffic_ops/testing/api/v13/tc-fixtures.json
+++ b/traffic_ops/testing/api/v13/tc-fixtures.json
@@ -1043,18 +1043,13 @@
"tenants": [
{
"active": true,
- "name": "root",
- "parentTenantName": null
- },
- {
- "active": true,
"name": "tenant1",
- "parentTenantName": "root"
+ "parentName": "root"
},
{
"active": false,
"name": "tenant2",
- "parentTenantName": "root"
+ "parentName": "root"
}
],
"types": [
@@ -1250,5 +1245,143 @@
"name": "TRAFFIC_MONITOR",
"useInTable": "server"
}
+ ],
+ "users": [
+ {
+ "addressLine1": "address of admin",
+ "addressLine2": "",
+ "city": "Anywhere",
+ "company": "Comcast",
+ "country": "USA",
+ "email": "admin@example.com",
+ "fullName": "Fred the admin",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "810-555-9876",
+ "postalCode": "55443",
+ "publicSshKey": "",
+ "role": 4,
+ "rolename": "admin",
+ "stateOrProvince": "LA",
+ "tenant": "root",
+ "uid": 0,
+ "username": "adminuser"
+ },
+ {
+ "addressLine1": "address of disallowed",
+ "addressLine2": "place",
+ "city": "somewhere",
+ "company": "else",
+ "country": "UK",
+ "email": "disallowed@example.com",
+ "fullName": "Me me",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "",
+ "postalCode": "",
+ "publicSshKey": "",
+ "registrationSent": "",
+ "role": 1,
+ "roleName": "disallowed",
+ "stateOrProvince": "",
+ "tenant": "root",
+ "tenant": 3,
+ "uid": 0,
+ "username": "disalloweduser"
+ },
+ {
+ "addressLine1": "address of read-only",
+ "addressLine2": "",
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "readonly@example.com",
+ "fullName": "",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "",
+ "postalCode": "",
+ "publicSshKey": "",
+ "registrationSent": "",
+ "role": 2,
+ "roleName": "read-only user",
+ "stateOrProvince": "",
+ "tenant": "root",
+ "tenant": 3,
+ "uid": 0,
+ "username": "readuser"
+ },
+ {
+ "addressLine1": "address of operations",
+ "addressLine2": "",
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "operations@example.com",
+ "fullName": "",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "",
+ "postalCode": "",
+ "publicSshKey": "",
+ "registrationSent": "",
+ "role": 3,
+ "roleName": "operations",
+ "stateOrProvince": "",
+ "tenant": "root",
+ "tenant": 4,
+ "uid": 0,
+ "username": "operationsuser"
+ },
+ {
+ "addressLine1": "",
+ "addressLine2": "",
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "portal@example.com",
+ "fullName": "",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "",
+ "postalCode": "",
+ "publicSshKey": "",
+ "registrationSent": "",
+ "role": 5,
+ "roleName": "portal",
+ "stateOrProvince": "",
+ "tenant": "root",
+ "tenant": 2,
+ "uid": 0,
+ "username": "portaluser"
+ },
+ {
+ "addressLine1": "",
+ "addressLine2": "",
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "federation@example.com",
+ "fullName": "",
+ "gid": 0,
+ "localPasswd": "pa$$word",
+ "newUser": false,
+ "phoneNumber": "",
+ "postalCode": "",
+ "publicSshKey": "",
+ "registrationSent": "",
+ "role": 7,
+ "roleName": "federation",
+ "stateOrProvince": "",
+ "tenant": "root",
+ "tenant": 4,
+ "uid": 0,
+ "username": "federationuser"
+ }
]
}
diff --git a/traffic_ops/testing/api/v13/tenants_test.go b/traffic_ops/testing/api/v13/tenants_test.go
new file mode 100644
index 0000000..feb205c
--- /dev/null
+++ b/traffic_ops/testing/api/v13/tenants_test.go
@@ -0,0 +1,136 @@
+package v13
+
+/*
+
+ 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.
+*/
+
+import (
+ "testing"
+
+ tc "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func TestTenants(t *testing.T) {
+
+ CreateTestTenants(t)
+ UpdateTestTenants(t)
+ GetTestTenants(t)
+ //DeleteTestTenants(t)
+}
+
+func CreateTestTenants(t *testing.T) {
+ for _, ten := range testData.Tenants {
+ // testData does not define ParentID -- look up by name and fill in
+ if ten.ParentID == 0 {
+ parents, _, err := TOSession.GetTenantByName(ten.ParentName)
+ if err != nil {
+ t.Errorf("parent tenant %s: %++v", ten.ParentName, err)
+ continue
+ }
+ ten.ParentID = parents[0].ID
+ }
+ resp, _, err := TOSession.CreateTenant(ten)
+ t.Logf("response: %++v", resp)
+
+ if err != nil {
+ t.Errorf("could not CREATE tenant %s: %v\n", ten.Name, err)
+ }
+ }
+}
+
+func GetTestTenants(t *testing.T) {
+ resp, _, err := TOSession.GetTenants()
+ if err != nil {
+ t.Errorf("cannot GET all tenants: %v - %v\n", err, resp)
+ return
+ }
+
+ // expect root and badTenant (defined in todb.go) + all defined in testData.Tenants
+ if len(resp) != 2+len(testData.Tenants) {
+ t.Errorf("expected %d tenants, got %d", 2+len(testData.Tenants), len(resp))
+ }
+
+ for _, ten := range testData.Tenants {
+ resp, _, err := TOSession.GetTenantByName(ten.Name)
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %v - %v\n", err, resp)
+ continue
+ }
+ if len(resp) != 1 {
+ t.Errorf("expected 1 tenant for %s, got %d", ten.Name, len(resp))
+ continue
+ }
+ }
+}
+
+func UpdateTestTenants(t *testing.T) {
+
+ // Retrieve the Tenant by name so we can get the id for the Update
+ name := "tenant2"
+ parentName := "tenant1"
+ resp, _, err := TOSession.GetTenantByName(name)
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %s - %v\n", name, err)
+ }
+ modTenant := resp[0]
+ resp, _, err = TOSession.GetTenantByName(parentName)
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %s - %v\n", name, err)
+ }
+ newParent := resp[0]
+ modTenant.ParentID = newParent.ID
+ var alert tc.Alerts
+ alert, _, err = TOSession.UpdateTenantByID(modTenant.ID, modTenant)
+ if err != nil {
+ t.Errorf("cannot UPDATE Tenant by id: %v - %v\n", err, alert)
+ }
+
+ // Retrieve the Tenant to check Tenant parent name got updated
+ resp, _, err = TOSession.GetTenantByID(modTenant.ID)
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %v - %v\n", name, err)
+ }
+ t.Logf("modified: %++v", resp)
+ respTenant := resp[0]
+ if respTenant.ParentName != parentName {
+ t.Errorf("results do not match actual: %s, expected: %s\n", respTenant.ParentName, parentName)
+ }
+
+}
+
+func DeleteTestTenants(t *testing.T) {
+
+ for _, ten := range testData.Tenants {
+ // Retrieve the Tenant by name so we can get the id for the Update
+ resp, _, err := TOSession.GetTenantByName(ten.Name)
+ if err != nil {
+ t.Errorf("cannot GET Tenant by name: %v - %v\n", ten.Name, err)
+ }
+ respTenant := resp[0]
+
+ delResp, _, err := TOSession.DeleteTenantByID(respTenant.ID)
+ if err != nil {
+ t.Errorf("cannot DELETE Tenant by name: %v - %v\n", err, delResp)
+ }
+
+ // Retrieve the Tenant to see if it got deleted
+ Tenants, _, err := TOSession.GetTenantByName(ten.Name)
+ if err != nil {
+ t.Errorf("error deleting Tenant name: %s\n", err.Error())
+ }
+ if len(Tenants) > 0 {
+ t.Errorf("expected Tenant name: %s to be deleted\n", ten.Name)
+ }
+ }
+}
diff --git a/traffic_ops/testing/api/v13/todb.go b/traffic_ops/testing/api/v13/todb.go
index 0081ee8..0ebea91 100644
--- a/traffic_ops/testing/api/v13/todb.go
+++ b/traffic_ops/testing/api/v13/todb.go
@@ -49,12 +49,6 @@ func OpenConnection() (*sql.DB, error) {
func SetupTestData(*sql.DB) error {
var err error
- err = SetupTenants(db)
- if err != nil {
- fmt.Printf("\nError setting up tenants %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
- os.Exit(1)
- }
-
err = SetupRoles(db)
if err != nil {
fmt.Printf("\nError setting up roles %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
@@ -73,6 +67,12 @@ func SetupTestData(*sql.DB) error {
os.Exit(1)
}
+ err = SetupTenants(db)
+ if err != nil {
+ fmt.Printf("\nError setting up tenant %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
+ os.Exit(1)
+ }
+
err = SetupTmusers(db)
if err != nil {
fmt.Printf("\nError setting up tm_user %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
@@ -136,12 +136,12 @@ func SetupTmusers(db *sql.DB) error {
// Creates users in different tenants
sqlStmt := `
-INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Disallowed + `','` + encryptedPassword + `','` + encryptedPassword + `', 1, 3);
-INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.ReadOnly + `','` + encryptedPassword + `','` + encryptedPassword + `', 2, 3);
-INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Operations + `','` + encryptedPassword + `','` + encryptedPassword + `', 3, 3);
+INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Disallowed + `','` + encryptedPassword + `','` + encryptedPassword + `', 1, 2);
+INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.ReadOnly + `','` + encryptedPassword + `','` + encryptedPassword + `', 2, 2);
+INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Operations + `','` + encryptedPassword + `','` + encryptedPassword + `', 3, 2);
INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Admin + `','` + encryptedPassword + `','` + encryptedPassword + `', 4, 2);
-INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Portal + `','` + encryptedPassword + `','` + encryptedPassword + `', 5, 3);
-INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Federation + `','` + encryptedPassword + `','` + encryptedPassword + `', 6, 3);
+INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Portal + `','` + encryptedPassword + `','` + encryptedPassword + `', 5, 2);
+INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_id) VALUES ('` + Config.TrafficOps.Users.Federation + `','` + encryptedPassword + `','` + encryptedPassword + `', 6, 2);
`
err = execSQL(db, sqlStmt, "tm_user")
if err != nil {
@@ -153,11 +153,11 @@ INSERT INTO tm_user (username, local_passwd, confirm_local_passwd, role, tenant_
// SetupTenants ...
func SetupTenants(db *sql.DB) error {
+ // TODO: root tenant must be present in initial database. "badtenant" is needed for now so tests can be done
+ // with a tenant outside the user's tenant. That should be removed once User API tests are in place rather than the SetupUsers defined above.
sqlStmt := `
INSERT INTO tenant (id, name, active, parent_id, last_updated) VALUES (1, 'root', true, null, '2018-01-19 19:01:21.327262');
-INSERT INTO tenant (id, name, active, parent_id, last_updated) VALUES (2, 'grandparent tenant', true, 1, '2018-01-19 19:01:21.327262');
-INSERT INTO tenant (id, name, active, parent_id, last_updated) VALUES (3, 'parent tenant', true, 2, '2018-01-19 19:01:21.327262');
-INSERT INTO tenant (id, name, active, parent_id, last_updated) VALUES (4, 'child tenant', true, 3, '2018-01-19 19:01:21.327262');
+INSERT INTO tenant (id, name, active, parent_id, last_updated) VALUES (2, 'badtenant', true, 1, '2018-01-19 19:01:21.327262');
`
err := execSQL(db, sqlStmt, "tenant")
if err != nil {
diff --git a/traffic_ops/testing/api/v13/traffic_control.go b/traffic_ops/testing/api/v13/traffic_control.go
index 98fcdfc..bbe7d13 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -39,6 +39,6 @@ type TrafficControl struct {
Roles []v13.Role `json:"roles"`
Servers []v13.Server `json:"servers"`
Statuses []v12.Status `json:"statuses"`
- Tenants []v12.Tenant `json:"tenants"`
+ Tenants []v13.Tenant `json:"tenants"`
Types []v12.Type `json:"types"`
}
[trafficcontrol] 03/15: add tenants structs
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 23aa135a32d47145bd614dafd925964d8936a17d
Author: Dan Kirkwood <da...@apache.org>
AuthorDate: Thu Jun 21 14:11:04 2018 -0600
add tenants structs
---
lib/go-tc/tenants.go | 62 +++++++++++++++++++++++++++++++++-------------------
1 file changed, 39 insertions(+), 23 deletions(-)
diff --git a/lib/go-tc/tenants.go b/lib/go-tc/tenants.go
index 232d617..c53e707 100644
--- a/lib/go-tc/tenants.go
+++ b/lib/go-tc/tenants.go
@@ -1,24 +1,23 @@
package tc
/*
-
- 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.
-*/
-
-// GetTenantsResponse ...
-type GetTenantsResponse struct {
- Response []Tenant `json:"response"`
-}
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
// TenantResponse ...
type TenantResponse struct {
@@ -26,13 +25,30 @@ type TenantResponse struct {
Alerts []TenantAlert `json:"alerts"`
}
+// TenantResponse ...
+type TenantsResponse struct {
+ Response []Tenant `json:"response"`
+ Alerts []TenantAlert `json:"alerts"`
+}
+
// Tenant ...
type Tenant struct {
- ID int `json:"id,omitempty"`
- Name string `json:"name,omitempty"`
- Active bool `json:"active,omitempty"`
- ParentID int `json:"parentId"`
- ParentName string `json:"parentName,omitempty"`
+ Active bool `json:"active"`
+ ID int `json:"id"`
+ LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
+ Name string `json:"name"`
+ ParentID int `json:"parentId"`
+ ParentName string `json:"parentName,omitempty" db:"parent_name"`
+}
+
+// TenantNullable ...
+type TenantNullable struct {
+ ID *int `json:"id" db:"id"`
+ Name *string `json:"name" db:"name"`
+ Active *bool `json:"active" db:"active"`
+ LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
+ ParentID *int `json:"parentId" db:"parent_id"`
+ ParentName *string `json:"parentName,omitempty" db:"parent_name"`
}
// DeleteTenantResponse ...