| // Package cachegroupparameter is deprecated and will be removed with API v1-3. |
| package cachegroupparameter |
| |
| /* |
| * 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 ( |
| "bytes" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "strconv" |
| "strings" |
| "time" |
| |
| "github.com/apache/trafficcontrol/lib/go-log" |
| "github.com/apache/trafficcontrol/lib/go-tc" |
| "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" |
| "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/parameter" |
| "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims" |
| |
| "github.com/jmoiron/sqlx" |
| ) |
| |
| const ( |
| CacheGroupIDQueryParam = "id" |
| CacheGroupIDNamedQueryParam = "cachegroupID" |
| ParameterIDQueryParam = "parameterId" |
| ) |
| |
| // TOCacheGroupParameter is a type alias that is used to define CRUD functions on. |
| type TOCacheGroupParameter struct { |
| api.APIInfoImpl `json:"-"` |
| tc.CacheGroupParameterNullable |
| CacheGroupID int `json:"-" db:"cachegroup_id"` |
| } |
| |
| func (cgparam *TOCacheGroupParameter) ParamColumns() map[string]dbhelpers.WhereColumnInfo { |
| return map[string]dbhelpers.WhereColumnInfo{ |
| CacheGroupIDQueryParam: dbhelpers.WhereColumnInfo{Column: "cgp.cachegroup", Checker: api.IsInt}, |
| ParameterIDQueryParam: dbhelpers.WhereColumnInfo{Column: "p.id", Checker: api.IsInt}, |
| } |
| } |
| |
| func (cgparam *TOCacheGroupParameter) GetType() string { |
| return "cachegroup parameter" |
| } |
| |
| func (cgparam *TOCacheGroupParameter) Read(h http.Header, useIMS bool) ([]interface{}, error, error, int, *time.Time) { |
| var maxTime time.Time |
| var runSecond bool |
| queryParamsToQueryCols := cgparam.ParamColumns() |
| parameters := cgparam.APIInfo().Params |
| where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(parameters, queryParamsToQueryCols) |
| if len(errs) > 0 { |
| return nil, util.JoinErrs(errs), nil, http.StatusBadRequest, nil |
| } |
| cgID, err := strconv.Atoi(parameters[CacheGroupIDQueryParam]) |
| if err != nil { |
| return nil, errors.New("cache group id must be an integer"), nil, http.StatusBadRequest, nil |
| } |
| |
| _, ok, err := dbhelpers.GetCacheGroupNameFromID(cgparam.ReqInfo.Tx.Tx, cgID) |
| if err != nil { |
| return nil, nil, err, http.StatusInternalServerError, nil |
| } else if !ok { |
| return nil, errors.New("cachegroup does not exist"), nil, http.StatusNotFound, nil |
| } |
| |
| params := []interface{}{} |
| if useIMS { |
| runSecond, maxTime = ims.TryIfModifiedSinceQuery(cgparam.ReqInfo.Tx, h, queryValues, selectMaxLastUpdatedQuery(where)) |
| if !runSecond { |
| log.Debugln("IMS HIT") |
| return params, nil, nil, http.StatusNotModified, &maxTime |
| } |
| log.Debugln("IMS MISS") |
| } else { |
| log.Debugln("Non IMS request") |
| } |
| |
| query := selectQuery() + where + orderBy + pagination |
| rows, err := cgparam.ReqInfo.Tx.NamedQuery(query, queryValues) |
| if err != nil { |
| return nil, nil, errors.New("querying " + cgparam.GetType() + ": " + err.Error()), http.StatusInternalServerError, nil |
| } |
| defer rows.Close() |
| |
| for rows.Next() { |
| var p tc.CacheGroupParameterNullable |
| if err = rows.StructScan(&p); err != nil { |
| return nil, nil, errors.New("scanning " + cgparam.GetType() + ": " + err.Error()), http.StatusInternalServerError, nil |
| } |
| if p.Secure != nil && *p.Secure && cgparam.ReqInfo.User.PrivLevel < auth.PrivLevelAdmin { |
| p.Value = ¶meter.HiddenField |
| } |
| params = append(params, p) |
| } |
| |
| return params, nil, nil, http.StatusOK, &maxTime |
| } |
| |
| func selectMaxLastUpdatedQuery(where string) string { |
| return `SELECT max(t) from ( |
| SELECT max(p.last_updated) as t FROM parameter p |
| LEFT JOIN cachegroup_parameter cgp ON cgp.parameter = p.id ` + where + |
| ` UNION ALL |
| select max(last_updated) as t from last_deleted l where l.table_name='parameter') as res` |
| } |
| |
| func selectQuery() string { |
| |
| query := `SELECT |
| p.config_file, |
| p.id, |
| p.last_updated, |
| p.name, |
| p.value, |
| p.secure |
| FROM parameter p |
| LEFT JOIN cachegroup_parameter cgp ON cgp.parameter = p.id` |
| return query |
| } |
| |
| // GetKeyFieldsInfo implements the api.Identifier interface. |
| func (cgparam *TOCacheGroupParameter) GetKeyFieldsInfo() []api.KeyFieldInfo { |
| return []api.KeyFieldInfo{ |
| { |
| Field: CacheGroupIDNamedQueryParam, |
| Func: api.GetIntKey, |
| }, |
| { |
| Field: ParameterIDQueryParam, |
| Func: api.GetIntKey, |
| }, |
| } |
| } |
| |
| // SetKeys implements the api.Identifier interface and allows the |
| // delete handler to assign cachegroup and parameter ids. |
| func (cgparam *TOCacheGroupParameter) SetKeys(keys map[string]interface{}) { |
| id, _ := keys[CacheGroupIDNamedQueryParam].(int) |
| cgparam.CacheGroupID = id |
| |
| paramID, _ := keys[ParameterIDQueryParam].(int) |
| cgparam.ID = ¶mID |
| } |
| |
| // DeleteQuery implements the api.GenericDeleter interface. |
| func (cgparam *TOCacheGroupParameter) DeleteQuery() string { |
| return `DELETE FROM cachegroup_parameter |
| WHERE cachegroup = :cachegroup_id AND parameter = :id` |
| } |
| |
| // GetAuditName implements the api.Identifier interface. |
| func (cgparam *TOCacheGroupParameter) GetAuditName() string { |
| if cgparam.ID != nil { |
| return strconv.Itoa(cgparam.CacheGroupID) + "-" + strconv.Itoa(*cgparam.ID) |
| } |
| return "unknown" |
| } |
| |
| // GetKeys implements the api.Identifier interface. |
| func (cgparam *TOCacheGroupParameter) GetKeys() (map[string]interface{}, bool) { |
| if cgparam.ID == nil { |
| return map[string]interface{}{ParameterIDQueryParam: 0}, false |
| } |
| return map[string]interface{}{ |
| CacheGroupIDNamedQueryParam: cgparam.CacheGroupID, |
| ParameterIDQueryParam: *cgparam.ID, |
| }, true |
| } |
| |
| // Delete implements the api.CRUDer interface. |
| func (cgparam *TOCacheGroupParameter) Delete() (error, error, int) { |
| _, ok, err := dbhelpers.GetCacheGroupNameFromID(cgparam.ReqInfo.Tx.Tx, cgparam.CacheGroupID) |
| if err != nil { |
| return nil, err, http.StatusInternalServerError |
| } else if !ok { |
| return fmt.Errorf("cachegroup %v does not exist", cgparam.CacheGroupID), nil, http.StatusNotFound |
| } |
| |
| _, ok, err = dbhelpers.GetParamNameByID(cgparam.ReqInfo.Tx.Tx, *cgparam.ID) |
| if err != nil { |
| return nil, err, http.StatusInternalServerError |
| } else if !ok { |
| return fmt.Errorf("parameter %v does not exist", *cgparam.ID), nil, http.StatusNotFound |
| } |
| |
| // CheckIfCurrentUserCanModifyCachegroup |
| userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCachegroup(cgparam.ReqInfo.Tx.Tx, cgparam.CacheGroupID, cgparam.ReqInfo.User.UserName) |
| if userErr != nil || sysErr != nil { |
| return userErr, sysErr, errCode |
| } |
| return api.GenericDelete(cgparam) |
| } |
| |
| // ReadAllCacheGroupParameters reads all cachegroup parameter associations. |
| func ReadAllCacheGroupParameters(w http.ResponseWriter, r *http.Request) { |
| inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) |
| if userErr != nil || sysErr != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr, nil) |
| return |
| } |
| defer inf.Close() |
| output, err := GetAllCacheGroupParameters(inf.Tx, inf.Params) |
| if err != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("querying cachegroupparameters with error: "+err.Error()), nil) |
| return |
| } |
| api.WriteAlertsObj(w, r, http.StatusOK, api.CreateDeprecationAlerts(nil), output) |
| } |
| |
| // GetAllCacheGroupParameters gets all cachegroup associations from the database and returns as slice. |
| func GetAllCacheGroupParameters(tx *sqlx.Tx, parameters map[string]string) (tc.CacheGroupParametersList, error) { |
| if _, ok := parameters["orderby"]; !ok { |
| parameters["orderby"] = "cachegroup" |
| } |
| |
| // Query Parameters to Database Query column mappings |
| // see the fields mapped in the SQL query |
| queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{ |
| "cachegroup": dbhelpers.WhereColumnInfo{Column: "cgp.cachegroup", Checker: api.IsInt}, |
| "parameter": dbhelpers.WhereColumnInfo{Column: "cgp.parameter", Checker: api.IsInt}, |
| } |
| |
| where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(parameters, queryParamsToQueryCols) |
| if len(errs) > 0 { |
| return tc.CacheGroupParametersList{}, util.JoinErrs(errs) |
| } |
| |
| query := selectAllQuery() + where + orderBy + pagination |
| rows, err := tx.NamedQuery(query, queryValues) |
| if err != nil { |
| return tc.CacheGroupParametersList{}, errors.New("querying cachegroupParameters: " + err.Error()) |
| } |
| defer rows.Close() |
| |
| paramsList := tc.CacheGroupParametersList{} |
| params := []tc.CacheGroupParametersResponseNullable{} |
| for rows.Next() { |
| var p tc.CacheGroupParametersNullable |
| if err = rows.Scan(&p.CacheGroup, &p.Parameter, &p.LastUpdated, &p.CacheGroupName); err != nil { |
| return tc.CacheGroupParametersList{}, errors.New("scanning cachegroupParameters: " + err.Error()) |
| } |
| params = append(params, tc.FormatForResponse(p)) |
| } |
| paramsList.CacheGroupParameters = params |
| return paramsList, nil |
| } |
| |
| // AddCacheGroupParameters performs a Create for cachegroup parameter associations. |
| // AddCacheGroupParameters accepts data as a single association or an array of multiple. |
| func AddCacheGroupParameters(w http.ResponseWriter, r *http.Request) { |
| inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) |
| if userErr != nil || sysErr != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr, nil) |
| return |
| } |
| defer inf.Close() |
| data, err := ioutil.ReadAll(r.Body) |
| if err != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("reading request body: "+err.Error()), nil, nil) |
| return |
| } |
| |
| buf := ioutil.NopCloser(bytes.NewReader(data)) |
| |
| var paramsInt interface{} |
| |
| decoder := json.NewDecoder(buf) |
| err = decoder.Decode(¶msInt) |
| if err != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parsing json: "+err.Error()), nil, nil) |
| return |
| } |
| |
| var params []tc.CacheGroupParametersNullable |
| _, ok := paramsInt.([]interface{}) |
| var parseErr error = nil |
| if !ok { |
| var singleParam tc.CacheGroupParametersNullable |
| parseErr = json.Unmarshal(data, &singleParam) |
| if singleParam.CacheGroup == nil || singleParam.Parameter == nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("invalid cachegroup parameter."), nil, nil) |
| return |
| } |
| params = append(params, singleParam) |
| } else { |
| parseErr = json.Unmarshal(data, ¶ms) |
| } |
| if parseErr != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parsing cachegroup parameter: "+parseErr.Error()), nil, nil) |
| return |
| } |
| cachegroups := []int{} |
| for _, p := range params { |
| ppExists, err := dbhelpers.CachegroupParameterAssociationExists(*p.Parameter, *p.CacheGroup, inf.Tx.Tx) |
| if err != nil { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err, nil) |
| return |
| } |
| if ppExists { |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parameter: "+strconv.Itoa(*p.Parameter)+" already associated with cachegroup: "+strconv.Itoa(*p.CacheGroup)+"."), nil, nil) |
| return |
| } |
| cachegroups = append(cachegroups, *p.CacheGroup) |
| } |
| userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCachegroups(inf.Tx.Tx, cachegroups, inf.User.UserName) |
| if userErr != nil || sysErr != nil { |
| api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) |
| return |
| } |
| values := []string{} |
| for _, param := range params { |
| values = append(values, "("+strconv.Itoa(*param.CacheGroup)+", "+strconv.Itoa(*param.Parameter)+")") |
| } |
| |
| insQuery := strings.Join(values, ", ") |
| _, err = inf.Tx.Tx.Query(insertQuery() + insQuery) |
| |
| if err != nil { |
| userErr, sysErr, code := api.ParseDBError(err) |
| api.HandleDeprecatedErr(w, r, inf.Tx.Tx, code, userErr, sysErr, nil) |
| return |
| } |
| |
| alerts := tc.CreateAlerts(tc.SuccessLevel, "Cachegroup parameter associations were created.") |
| alerts.AddAlerts(api.CreateDeprecationAlerts(nil)) |
| api.WriteAlertsObj(w, r, http.StatusOK, alerts, params) |
| } |
| |
| func selectAllQuery() string { |
| return `SELECT cgp.cachegroup, cgp.parameter, cgp.last_updated, cg.name |
| FROM cachegroup_parameter AS cgp |
| JOIN cachegroup AS cg ON cg.id = cachegroup` |
| } |
| |
| func insertQuery() string { |
| return `INSERT INTO cachegroup_parameter |
| (cachegroup, |
| parameter) |
| VALUES ` |
| } |