mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 21:35:52 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
179 lines
5.3 KiB
Go
179 lines
5.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
)
|
|
|
|
const ConfigEntryNotFoundErr string = "Config entry not found"
|
|
|
|
// Config switches on the different CRUD operations for config entries.
|
|
func (s *HTTPHandlers) Config(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
switch req.Method {
|
|
case "GET":
|
|
return s.configGet(resp, req)
|
|
|
|
case "DELETE":
|
|
return s.configDelete(resp, req)
|
|
|
|
default:
|
|
return nil, MethodNotAllowedError{req.Method, []string{"GET", "DELETE"}}
|
|
}
|
|
}
|
|
|
|
// configGet gets either a specific config entry, or lists all config entries
|
|
// of a kind if no name is provided.
|
|
func (s *HTTPHandlers) configGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
var args structs.ConfigEntryQuery
|
|
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
|
return nil, nil
|
|
}
|
|
kindAndName := strings.TrimPrefix(req.URL.Path, "/v1/config/")
|
|
pathArgs := strings.SplitN(kindAndName, "/", 2)
|
|
|
|
switch len(pathArgs) {
|
|
case 2:
|
|
// Both kind/name provided.
|
|
args.Kind = pathArgs[0]
|
|
args.Name = pathArgs[1]
|
|
|
|
if err := s.parseEntMetaForConfigEntryKind(args.Kind, req, &args.EnterpriseMeta); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var reply structs.ConfigEntryResponse
|
|
if err := s.agent.RPC(req.Context(), "ConfigEntry.Get", &args, &reply); err != nil {
|
|
return nil, err
|
|
}
|
|
setMeta(resp, &reply.QueryMeta)
|
|
|
|
if reply.Entry == nil {
|
|
return nil, HTTPError{StatusCode: http.StatusNotFound, Reason: fmt.Sprintf("%s for %q / %q", ConfigEntryNotFoundErr, pathArgs[0], pathArgs[1])}
|
|
}
|
|
|
|
return reply.Entry, nil
|
|
case 1:
|
|
if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil {
|
|
return nil, err
|
|
}
|
|
// Only kind provided, list entries.
|
|
args.Kind = pathArgs[0]
|
|
|
|
var reply structs.IndexedConfigEntries
|
|
if err := s.agent.RPC(req.Context(), "ConfigEntry.List", &args, &reply); err != nil {
|
|
return nil, err
|
|
}
|
|
setMeta(resp, &reply.QueryMeta)
|
|
|
|
return reply.Entries, nil
|
|
default:
|
|
return nil, HTTPError{StatusCode: http.StatusNotFound, Reason: "Must provide either a kind or both kind and name"}
|
|
}
|
|
}
|
|
|
|
// configDelete deletes the given config entry.
|
|
func (s *HTTPHandlers) configDelete(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
var args structs.ConfigEntryRequest
|
|
s.parseDC(req, &args.Datacenter)
|
|
s.parseToken(req, &args.Token)
|
|
kindAndName := strings.TrimPrefix(req.URL.Path, "/v1/config/")
|
|
pathArgs := strings.SplitN(kindAndName, "/", 2)
|
|
|
|
if len(pathArgs) != 2 {
|
|
return nil, HTTPError{StatusCode: http.StatusNotFound, Reason: "Must provide both a kind and name to delete"}
|
|
}
|
|
|
|
entry, err := structs.MakeConfigEntry(pathArgs[0], pathArgs[1])
|
|
if err != nil {
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: err.Error()}
|
|
}
|
|
args.Entry = entry
|
|
// Parse enterprise meta.
|
|
meta := args.Entry.GetEnterpriseMeta()
|
|
|
|
if err := s.parseEntMetaForConfigEntryKind(entry.GetKind(), req, meta); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check for cas value
|
|
if casStr := req.URL.Query().Get("cas"); casStr != "" {
|
|
casVal, err := strconv.ParseUint(casStr, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args.Op = structs.ConfigEntryDeleteCAS
|
|
args.Entry.GetRaftIndex().ModifyIndex = casVal
|
|
}
|
|
|
|
var reply structs.ConfigEntryDeleteResponse
|
|
if err := s.agent.RPC(req.Context(), "ConfigEntry.Delete", &args, &reply); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Return the `deleted` boolean for CAS operations, but not normal deletions
|
|
// to maintain backwards-compatibility with existing callers.
|
|
if args.Op == structs.ConfigEntryDeleteCAS {
|
|
return reply.Deleted, nil
|
|
}
|
|
return struct{}{}, nil
|
|
}
|
|
|
|
// ConfigApply applies the given config entry update.
|
|
func (s *HTTPHandlers) ConfigApply(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
args := structs.ConfigEntryRequest{
|
|
Op: structs.ConfigEntryUpsert,
|
|
}
|
|
s.parseDC(req, &args.Datacenter)
|
|
s.parseToken(req, &args.Token)
|
|
|
|
var raw map[string]interface{}
|
|
if err := decodeBodyDeprecated(req, &raw, nil); err != nil {
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
|
}
|
|
|
|
if entry, err := structs.DecodeConfigEntry(raw); err == nil {
|
|
args.Entry = entry
|
|
} else {
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
|
}
|
|
|
|
// Parse enterprise meta.
|
|
var meta acl.EnterpriseMeta
|
|
if err := s.parseEntMetaForConfigEntryKind(args.Entry.GetKind(), req, &meta); err != nil {
|
|
return nil, err
|
|
}
|
|
args.Entry.GetEnterpriseMeta().Merge(&meta)
|
|
|
|
// Check for cas value
|
|
if casStr := req.URL.Query().Get("cas"); casStr != "" {
|
|
casVal, err := strconv.ParseUint(casStr, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args.Op = structs.ConfigEntryUpsertCAS
|
|
args.Entry.GetRaftIndex().ModifyIndex = casVal
|
|
}
|
|
|
|
var reply bool
|
|
if err := s.agent.RPC(req.Context(), "ConfigEntry.Apply", &args, &reply); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return reply, nil
|
|
}
|
|
|
|
func (s *HTTPHandlers) parseEntMetaForConfigEntryKind(kind string, req *http.Request, entMeta *acl.EnterpriseMeta) error {
|
|
if kind == structs.ServiceIntentions {
|
|
return s.parseEntMeta(req, entMeta)
|
|
}
|
|
return s.parseEntMetaNoWildcard(req, entMeta)
|
|
}
|