blob: 1cd84acbd46cbab2c7c77e4fd5ebb5badfb1baad [file] [log] [blame]
/*
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 v3
import (
"net/http"
"strings"
"testing"
"time"
"github.com/apache/trafficcontrol/lib/go-rfc"
tc "github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
)
func TestProfiles(t *testing.T) {
WithObjs(t, []TCObj{CDNs, Types, Profiles, Parameters}, func() {
CreateBadProfiles(t)
UpdateTestProfiles(t)
currentTime := time.Now().UTC().Add(-5 * time.Second)
time := currentTime.Format(time.RFC1123)
var header http.Header
header = make(map[string][]string)
header.Set(rfc.IfUnmodifiedSince, time)
UpdateTestProfilesWithHeaders(t, header)
GetTestProfilesIMS(t)
GetTestProfiles(t)
GetTestProfilesWithParameters(t)
ImportProfile(t)
CopyProfile(t)
header = make(map[string][]string)
etag := rfc.ETag(currentTime)
header.Set(rfc.IfMatch, etag)
UpdateTestProfilesWithHeaders(t, header)
})
}
func UpdateTestProfilesWithHeaders(t *testing.T, header http.Header) {
if len(testData.Profiles) > 0 {
firstProfile := testData.Profiles[0]
// Retrieve the Profile by name so we can get the id for the Update
resp, _, err := TOSession.GetProfileByNameWithHdr(firstProfile.Name, header)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", firstProfile.Name, err)
}
if len(resp) > 0 {
remoteProfile := resp[0]
_, reqInf, err := TOSession.UpdateProfileByIDWithHdr(remoteProfile.ID, remoteProfile, header)
if err == nil {
t.Errorf("Expected error about precondition failed, but got none")
}
if reqInf.StatusCode != http.StatusPreconditionFailed {
t.Errorf("Expected status code 412, got %v", reqInf.StatusCode)
}
}
}
}
func GetTestProfilesIMS(t *testing.T) {
var header http.Header
header = make(map[string][]string)
futureTime := time.Now().AddDate(0, 0, 1)
time := futureTime.Format(time.RFC1123)
header.Set(rfc.IfModifiedSince, time)
for _, pr := range testData.Profiles {
_, reqInf, err := TOSession.GetProfileByNameWithHdr(pr.Name, header)
if err != nil {
t.Fatalf("Expected no error, but got %v", err.Error())
}
if reqInf.StatusCode != http.StatusNotModified {
t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
}
if len(pr.Parameters) > 0 {
parameter := pr.Parameters[0]
respParameter, _, err := TOSession.GetParameterByName(*parameter.Name)
if err != nil {
t.Errorf("Cannot GET Parameter by name: %v", err)
}
if len(respParameter) > 0 {
parameterID := respParameter[0].ID
if parameterID > 0 {
resp, _, err := TOSession.GetProfileByParameterIdWithHdr(parameterID, nil)
if err != nil {
t.Fatalf("Expected no error, but got %v", err.Error())
}
if reqInf.StatusCode != http.StatusNotModified {
t.Fatalf("Expected 304 status code, got %v", reqInf.StatusCode)
}
if len(resp) < 1 {
t.Errorf("Expected atleast one response for Get Profile by Parameters, but found %d", len(resp))
}
} else {
t.Errorf("Invalid parameter ID %d", parameterID)
}
} else {
t.Errorf("No response found for GET Parameters by name")
}
}
}
}
// CreateBadProfiles ensures that profiles can't be created with bad values
func CreateBadProfiles(t *testing.T) {
// blank profile
prs := []tc.Profile{
tc.Profile{Type: "", Name: "", Description: "", CDNID: 0},
tc.Profile{Type: "ATS_PROFILE", Name: "badprofile", Description: "description", CDNID: 0},
tc.Profile{Type: "ATS_PROFILE", Name: "badprofile", Description: "", CDNID: 1},
tc.Profile{Type: "ATS_PROFILE", Name: "", Description: "description", CDNID: 1},
tc.Profile{Type: "", Name: "badprofile", Description: "description", CDNID: 1},
}
for _, pr := range prs {
resp, _, err := TOSession.CreateProfile(pr)
if err == nil {
t.Errorf("Creating bad profile succeeded: %+v\nResponse is %+v", pr, resp)
}
}
}
func CopyProfile(t *testing.T) {
testCases := []struct {
description string
profile tc.ProfileCopy
expectedResp string
err string
}{
{
description: "copy profile",
profile: tc.ProfileCopy{
Name: "profile-2",
ExistingName: "EDGE1",
},
expectedResp: "created new profile [profile-2] from existing profile [EDGE1]",
},
{
description: "existing profile does not exist",
profile: tc.ProfileCopy{
Name: "profile-3",
ExistingName: "bogus",
},
err: "profile with name bogus does not exist",
},
{
description: "new profile already exists",
profile: tc.ProfileCopy{
Name: "EDGE2",
ExistingName: "EDGE1",
},
err: "profile with name EDGE2 already exists",
},
}
var newProfileNames []string
for _, c := range testCases {
t.Run(c.description, func(t *testing.T) {
resp, _, err := TOSession.CopyProfile(c.profile)
if c.err != "" {
if err != nil && !strings.Contains(err.Error(), c.err) {
t.Fatalf("got err= %s; expected err= %s", err, c.err)
}
} else if err != nil {
t.Fatalf("got err= %s; expected err= nil", err)
}
if err == nil {
if got, want := resp.Alerts.ToStrings()[0], c.expectedResp; got != want {
t.Fatalf("got= %s; expected= %s", got, want)
}
newProfileNames = append(newProfileNames, c.profile.Name)
}
})
}
// Cleanup profiles
for _, name := range newProfileNames {
profiles, _, err := TOSession.GetProfileByName(name)
if err != nil {
t.Fatalf("got err= %s; expected err= nil", err)
}
if len(profiles) == 0 {
t.Errorf("could not GET profile %+v: not found", name)
}
_, _, err = TOSession.DeleteProfileByID(profiles[0].ID)
if err != nil {
t.Fatalf("got err= %s; expected err= nil", err)
}
}
}
func CreateTestProfiles(t *testing.T) {
for _, pr := range testData.Profiles {
resp, _, err := TOSession.CreateProfile(pr)
t.Log("Response: ", resp)
if err != nil {
t.Errorf("could not CREATE profiles with name: %s %v", pr.Name, err)
}
profiles, _, err := TOSession.GetProfileByName(pr.Name)
if err != nil {
t.Errorf("could not GET profile with name: %s %v", pr.Name, err)
}
if len(profiles) == 0 {
t.Errorf("could not GET profile %+v: not found", pr)
}
profileID := profiles[0].ID
for _, param := range pr.Parameters {
if param.Name == nil || param.Value == nil || param.ConfigFile == nil {
t.Errorf("invalid parameter specification: %+v", param)
continue
}
_, _, err := TOSession.CreateParameter(tc.Parameter{Name: *param.Name, Value: *param.Value, ConfigFile: *param.ConfigFile})
if err != nil {
// ok if already exists
if !strings.Contains(err.Error(), "already exists") {
t.Errorf("could not CREATE parameter %+v: %s", param, err.Error())
continue
}
}
p, _, err := TOSession.GetParameterByNameAndConfigFileAndValue(*param.Name, *param.ConfigFile, *param.Value)
if err != nil {
t.Errorf("could not GET parameter %+v: %s", param, err.Error())
}
if len(p) == 0 {
t.Errorf("could not GET parameter %+v: not found", param)
}
_, _, err = TOSession.CreateProfileParameter(tc.ProfileParameter{ProfileID: profileID, ParameterID: p[0].ID})
if err != nil {
t.Errorf("could not CREATE profile_parameter %+v: %s", param, err.Error())
}
}
}
}
func UpdateTestProfiles(t *testing.T) {
firstProfile := testData.Profiles[0]
// Retrieve the Profile by name so we can get the id for the Update
resp, _, err := TOSession.GetProfileByName(firstProfile.Name)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", firstProfile.Name, err)
}
remoteProfile := resp[0]
expectedProfileDesc := "UPDATED"
remoteProfile.Description = expectedProfileDesc
var alert tc.Alerts
alert, _, err = TOSession.UpdateProfileByID(remoteProfile.ID, remoteProfile)
if err != nil {
t.Errorf("cannot UPDATE Profile by id: %v - %v", err, alert)
}
// Retrieve the Profile to check Profile name got updated
resp, _, err = TOSession.GetProfileByID(remoteProfile.ID)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", firstProfile.Name, err)
}
respProfile := resp[0]
if respProfile.Description != expectedProfileDesc {
t.Errorf("results do not match actual: %s, expected: %s", respProfile.Description, expectedProfileDesc)
}
}
func GetTestProfiles(t *testing.T) {
for _, pr := range testData.Profiles {
resp, _, err := TOSession.GetProfileByName(pr.Name)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", err, resp)
}
profileID := resp[0].ID
if len(pr.Parameters) > 0 {
parameter := pr.Parameters[0]
respParameter, _, err := TOSession.GetParameterByName(*parameter.Name)
if err != nil {
t.Errorf("Cannot GET Parameter by name: %v", err)
}
if len(respParameter) > 0 {
parameterID := respParameter[0].ID
if parameterID > 0 {
resp, _, err = TOSession.GetProfileByParameterId(parameterID)
if err != nil {
t.Errorf("cannot GET Profile by param: %v - %v", err, resp)
}
if len(resp) < 1 {
t.Errorf("Expected atleast one response for Get Profile by Parameters, but found %d", len(resp))
}
} else {
t.Errorf("Invalid parameter ID %d", parameterID)
}
} else {
t.Errorf("No response found for GET Parameters by name")
}
}
resp, _, err = TOSession.GetProfileByCDNID(pr.CDNID)
if err != nil {
t.Errorf("cannot GET Profile by cdn: %v - %v", err, resp)
}
// Export Profile
exportResp, _, err := TOSession.ExportProfile(profileID)
if err != nil {
t.Errorf("error exporting Profile: %v - %v", profileID, err)
}
if exportResp == nil {
t.Error("error exporting Profile: response nil")
}
}
}
func ImportProfile(t *testing.T) {
// Get ID of Profile to export
resp, _, err := TOSession.GetProfileByName(testData.Profiles[0].Name)
if err != nil {
t.Fatalf("cannot GET Profile by name: %v - %v", err, resp)
}
if resp == nil {
t.Fatal("error getting Profile: response nil")
}
if len(resp) != 1 {
t.Fatalf("Profiles expected 1, actual %v", len(resp))
}
profileID := resp[0].ID
// Export Profile to import
exportResp, _, err := TOSession.ExportProfile(profileID)
if err != nil {
t.Fatalf("error exporting Profile: %v - %v", profileID, err)
}
if exportResp == nil {
t.Fatal("error exporting Profile: response nil")
}
// Modify Profile and import
// Add parameter and change name
profile := exportResp.Profile
profile.Name = util.StrPtr("TestProfileImport")
newParam := tc.ProfileExportImportParameterNullable{
ConfigFile: util.StrPtr("config_file_import_test"),
Name: util.StrPtr("param_import_test"),
Value: util.StrPtr("import_test"),
}
parameters := append(exportResp.Parameters, newParam)
// Import Profile
importReq := tc.ProfileImportRequest{
Profile: profile,
Parameters: parameters,
}
importResp, _, err := TOSession.ImportProfile(&importReq)
if err != nil {
t.Fatalf("error importing Profile: %v - %v", profileID, err)
}
if importResp == nil {
t.Error("error importing Profile: response nil")
}
// Add newly create profile and parameter to testData so it gets deleted
testData.Profiles = append(testData.Profiles, tc.Profile{
Name: *profile.Name,
CDNName: *profile.CDNName,
Description: *profile.Description,
Type: *profile.Type,
})
testData.Parameters = append(testData.Parameters, tc.Parameter{
ConfigFile: *newParam.ConfigFile,
Name: *newParam.Name,
Value: *newParam.Value,
})
}
func GetTestProfilesWithParameters(t *testing.T) {
firstProfile := testData.Profiles[0]
resp, _, err := TOSession.GetProfileByName(firstProfile.Name)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", err, resp)
return
}
if len(resp) == 0 {
t.Errorf("cannot GET Profile by name: not found - %v", resp)
return
}
respProfile := resp[0]
// query by name does not retrieve associated parameters. But query by id does.
resp, _, err = TOSession.GetProfileByID(respProfile.ID)
if err != nil {
t.Errorf("cannot GET Profile by name: %v - %v", err, resp)
}
if len(resp) > 0 {
respProfile = resp[0]
respParameters := respProfile.Parameters
if len(respParameters) == 0 {
t.Errorf("expected a profile with parameters to be retrieved: %v - %v", err, respParameters)
}
}
}
func DeleteTestProfiles(t *testing.T) {
for _, pr := range testData.Profiles {
// Retrieve the Profile by name so we can get the id for the Update
resp, _, err := TOSession.GetProfileByName(pr.Name)
if err != nil {
t.Errorf("cannot GET Profile by name: %s - %v", pr.Name, err)
continue
}
if len(resp) == 0 {
t.Errorf("cannot GET Profile by name: not found - %s", pr.Name)
continue
}
profileID := resp[0].ID
// query by name does not retrieve associated parameters. But query by id does.
resp, _, err = TOSession.GetProfileByID(profileID)
if err != nil {
t.Errorf("cannot GET Profile by id: %v - %v", err, resp)
}
// delete any profile_parameter associations first
// the parameter is what's being deleted, but the delete is cascaded to profile_parameter
for _, param := range resp[0].Parameters {
_, _, err := TOSession.DeleteParameterByID(*param.ID)
if err != nil {
t.Errorf("cannot DELETE parameter with parameterID %d: %s", *param.ID, err.Error())
}
}
delResp, _, err := TOSession.DeleteProfileByID(profileID)
if err != nil {
t.Errorf("cannot DELETE Profile by name: %v - %v", err, delResp)
}
//time.Sleep(1 * time.Second)
// Retrieve the Profile to see if it got deleted
prs, _, err := TOSession.GetProfileByName(pr.Name)
if err != nil {
t.Errorf("error deleting Profile name: %s", err.Error())
}
if len(prs) > 0 {
t.Errorf("expected Profile Name: %s to be deleted", pr.Name)
}
// Attempt to export Profile
_, _, err = TOSession.ExportProfile(profileID)
if err == nil {
t.Errorf("export deleted profile %s - expected: error, actual: nil", pr.Name)
}
}
}