blob: 95baca4fbd80117f6b5d7367f893c7a5a7ab677c [file] [log] [blame]
package tc
import (
"bytes"
"database/sql"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/lib/pq"
)
/*
* 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.
*/
// ParametersResponse is the type of the response from Traffic Ops to GET
// requests made to the /parameters and /profiles/name/{{Name}}/parameters
// endpoints of its API.
type ParametersResponse struct {
Response []Parameter `json:"response"`
Alerts
}
// A Parameter defines some configuration setting (which is usually but
// definitely not always a line in a configuration file) used by some Profile
// or Cache Group.
type Parameter struct {
ConfigFile string `json:"configFile" db:"config_file"`
ID int `json:"id" db:"id"`
LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
Name string `json:"name" db:"name"`
Profiles json.RawMessage `json:"profiles" db:"profiles"`
Secure bool `json:"secure" db:"secure"`
Value string `json:"value" db:"value"`
}
// ParameterNullable is exactly like Parameter except that its properties are
// reference values, so they can be nil.
type ParameterNullable struct {
//
// NOTE: the db: struct tags are used for testing to map to their equivalent database column (if there is one)
//
ConfigFile *string `json:"configFile" db:"config_file"`
ID *int `json:"id" db:"id"`
LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
Name *string `json:"name" db:"name"`
Profiles json.RawMessage `json:"profiles" db:"profiles"`
Secure *bool `json:"secure" db:"secure"`
Value *string `json:"value" db:"value"`
}
// ProfileParameterByName is a structure that's used to represent a Parameter
// in a context where they are associated with some Profile specified by a
// client of the Traffic Ops API by its Name.
type ProfileParameterByName struct {
ConfigFile string `json:"configFile"`
ID int `json:"id"`
LastUpdated TimeNoMod `json:"lastUpdated"`
Name string `json:"name"`
Secure bool `json:"secure"`
Value string `json:"value"`
}
// ProfileParameterByNamePost is a structure that's only used internally to
// represent a Parameter that has been requested by a client of the Traffic Ops
// API to be associated with some Profile which was specified by Name.
//
// TODO: This probably shouldn't exist, or at least not be in the lib.
type ProfileParameterByNamePost struct {
ConfigFile *string `json:"configFile"`
Name *string `json:"name"`
Secure *int `json:"secure"`
Value *string `json:"value"`
}
// Validate implements the
// github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api.ParseValidator
// interface.
func (p *ProfileParameterByNamePost) Validate(tx *sql.Tx) []error {
return validateProfileParamPostFields(p.ConfigFile, p.Name, p.Value, p.Secure)
}
func validateProfileParamPostFields(configFile, name, value *string, secure *int) []error {
errs := []string{}
if configFile == nil || *configFile == "" {
errs = append(errs, "configFile")
}
if name == nil || *name == "" {
errs = append(errs, "name")
}
if secure == nil {
errs = append(errs, "secure")
}
if value == nil {
errs = append(errs, "value")
}
if len(errs) > 0 {
return []error{errors.New("required fields missing: " + strings.Join(errs, ", "))}
}
return nil
}
// ProfileParametersByNamePost is the object posted to profile/name/parameter endpoints. This object may be posted as either a single JSON object, or an array of objects. Either will unmarshal into this object; a single object will unmarshal into an array of 1 element.
type ProfileParametersByNamePost []ProfileParameterByNamePost
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (pp *ProfileParametersByNamePost) UnmarshalJSON(bts []byte) error {
bts = bytes.TrimLeft(bts, " \n\t\r")
if len(bts) == 0 {
return errors.New("no body")
}
if bts[0] == '[' {
ppArr := []ProfileParameterByNamePost{}
err := json.Unmarshal(bts, &ppArr)
*pp = ppArr
return err
}
p := ProfileParameterByNamePost{}
if err := json.Unmarshal(bts, &p); err != nil {
return err
}
*pp = append(*pp, p)
return nil
}
// Validate implements the
// github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api.ParseValidator
// interface.
func (pp *ProfileParametersByNamePost) Validate(tx *sql.Tx) error {
errs := []error{}
ppArr := ([]ProfileParameterByNamePost)(*pp)
for i, profileParam := range ppArr {
if ppErrs := profileParam.Validate(tx); len(ppErrs) > 0 {
for _, err := range ppErrs {
errs = append(errs, errors.New("object "+strconv.Itoa(i)+": "+err.Error()))
}
}
}
if len(errs) > 0 {
return util.JoinErrs(errs)
}
return nil
}
// ProfileParameterPostRespObj is a single Parameter in the Parameters slice of
// a ProfileParameterPostResp.
type ProfileParameterPostRespObj struct {
ProfileParameterByNamePost
ID int64 `json:"id"`
}
// ProfileParameterPostResp is the type of the `response` property of responses
// from Traffic Ops to POST requests made to its
// /profiles/name/{{Name}}/parameters API endpoint.
type ProfileParameterPostResp struct {
Parameters []ProfileParameterPostRespObj `json:"parameters"`
ProfileID int `json:"profileId"`
ProfileName string `json:"profileName"`
}
// A PostProfileParam is a request to associate zero or more Parameters with a
// particular Profile.
type PostProfileParam struct {
ProfileID *int64 `json:"profileId"`
ParamIDs *[]int64 `json:"paramIds"`
Replace *bool `json:"replace"`
}
// Sanitize ensures that Replace is not nil, setting it to false if it is.
//
// TODO: Figure out why this is taking a db transaction - should this be moved?
func (pp *PostProfileParam) Sanitize(tx *sql.Tx) {
if pp.Replace == nil {
pp.Replace = util.BoolPtr(false)
}
}
// Validate implements the
// github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api.ParseValidator
// interface.
func (pp *PostProfileParam) Validate(tx *sql.Tx) error {
pp.Sanitize(tx)
errs := []error{}
if pp.ProfileID == nil {
errs = append(errs, errors.New("profileId missing"))
} else if ok, err := ProfileExistsByID(*pp.ProfileID, tx); err != nil {
errs = append(errs, errors.New("checking profile ID "+strconv.Itoa(int(*pp.ProfileID))+" existence: "+err.Error()))
} else if !ok {
errs = append(errs, errors.New("no profile with ID "+strconv.Itoa(int(*pp.ProfileID))+" exists"))
}
if pp.ParamIDs == nil {
errs = append(errs, errors.New("paramIds missing"))
} else if ok, err := ParamsExist(*pp.ParamIDs, tx); err != nil {
errs = append(errs, errors.New(fmt.Sprintf("checking parameter IDs %v existence: "+err.Error(), *pp.ParamIDs)))
} else if !ok {
errs = append(errs, errors.New(fmt.Sprintf("parameters with IDs %v don't all exist", *pp.ParamIDs)))
}
if len(errs) > 0 {
return util.JoinErrs(errs)
}
return nil
}
// A PostParamProfile is a request to associate a particular Parameter with
// zero or more Profiles.
type PostParamProfile struct {
ParamID *int64 `json:"paramId"`
ProfileIDs *[]int64 `json:"profileIds"`
Replace *bool `json:"replace"`
}
// Sanitize ensures that Replace is not nil, setting it to false if it is.
//
// TODO: Figure out why this is taking a db transaction - should this be moved?
func (pp *PostParamProfile) Sanitize(tx *sql.Tx) {
if pp.Replace == nil {
pp.Replace = util.BoolPtr(false)
}
}
// Validate implements the
// github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api.ParseValidator
// interface.
func (pp *PostParamProfile) Validate(tx *sql.Tx) error {
pp.Sanitize(tx)
errs := []error{}
if pp.ParamID == nil {
errs = append(errs, errors.New("paramId missing"))
} else if ok, err := ParamExists(*pp.ParamID, tx); err != nil {
errs = append(errs, errors.New("checking param ID "+strconv.Itoa(int(*pp.ParamID))+" existence: "+err.Error()))
} else if !ok {
errs = append(errs, errors.New("no parameter with ID "+strconv.Itoa(int(*pp.ParamID))+" exists"))
}
if pp.ProfileIDs == nil {
errs = append(errs, errors.New("profileIds missing"))
} else if ok, err := ProfilesExistByIDs(*pp.ProfileIDs, tx); err != nil {
errs = append(errs, errors.New(fmt.Sprintf("checking profiles IDs %v existence: "+err.Error(), *pp.ProfileIDs)))
} else if !ok {
errs = append(errs, errors.New(fmt.Sprintf("profiles with IDs %v don't all exist", *pp.ProfileIDs)))
}
if len(errs) > 0 {
return util.JoinErrs(errs)
}
return nil
}
// ParamExists returns whether a parameter with the given id exists, and any error.
// TODO move to helper package.
func ParamExists(id int64, tx *sql.Tx) (bool, error) {
count := 0
if err := tx.QueryRow(`SELECT count(*) from parameter where id = $1`, id).Scan(&count); err != nil {
return false, errors.New("querying param existence from id: " + err.Error())
}
return count > 0, nil
}
// ParamsExist returns whether parameters exist for all the given ids, and any error.
// TODO move to helper package.
func ParamsExist(ids []int64, tx *sql.Tx) (bool, error) {
count := 0
if err := tx.QueryRow(`SELECT count(*) from parameter where id = ANY($1)`, pq.Array(ids)).Scan(&count); err != nil {
return false, errors.New("querying parameters existence from id: " + err.Error())
}
return count == len(ids), nil
}
// ProfileParametersNullable is an object of the form returned by the Traffic Ops /profileparameters endpoint.
type ProfileParametersNullable struct {
LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
Profile *string `json:"profile" db:"profile"`
Parameter *int `json:"parameter" db:"parameter_id"`
}
// ProfileParametersNullableResponse is the structure of a response from
// Traffic Ops to GET requests made to its /profileparameters API endpoint.
//
// TODO: This is only used internally in a v3 client method (not its call
// signature) - deprecate? Remove?
type ProfileParametersNullableResponse struct {
Response []ProfileParametersNullable `json:"response"`
}
// ProfileParam is a relationship between a Profile and some Parameter
// assigned to it as it appears in the Traffic Ops API's responses to the
// /profileparameters endpoint.
type ProfileParam struct {
// Parameter is the ID of the Parameter.
Parameter int `json:"parameter"`
// Profile is the name of the Profile to which the Parameter is assigned.
Profile string `json:"profile"`
LastUpdated *TimeNoMod `json:"lastUpdated"`
}
// ProfileParameterCreationRequest is the type of data accepted by Traffic
// Ops as payloads in POST requests to its /profileparameters endpoint.
type ProfileParameterCreationRequest struct {
ParameterID int `json:"parameterId"`
ProfileID int `json:"profileId"`
}
// ProfileParametersAPIResponse is the type of a response from Traffic Ops to
// requests made to its /profileparameters endpoint.
type ProfileParametersAPIResponse struct {
Response []ProfileParam `json:"response"`
Alerts
}
// ProfileExportImportParameterNullable is an object of the form used by Traffic Ops
// to represent parameters for exported and imported profiles.
type ProfileExportImportParameterNullable struct {
ConfigFile *string `json:"config_file"`
Name *string `json:"name"`
Value *string `json:"value"`
}