From 826e6a1cca5508b064cc2804beabc21d3dcce091 Mon Sep 17 00:00:00 2001 From: Alex Clemmer Date: Fri, 28 Jun 2019 10:07:49 -0700 Subject: [PATCH] Add `pulumi policy apply` command --- cmd/policy.go | 1 + cmd/policy_apply.go | 57 ++++++++++++++++++++++++++ pkg/apitype/policy.go | 6 +++ pkg/backend/httpstate/client/client.go | 21 ++++++++++ pkg/backend/httpstate/policypack.go | 4 ++ pkg/backend/policypack.go | 8 ++++ 6 files changed, 97 insertions(+) create mode 100644 cmd/policy_apply.go diff --git a/cmd/policy.go b/cmd/policy.go index 3db61eb17..d7d06fb55 100644 --- a/cmd/policy.go +++ b/cmd/policy.go @@ -28,6 +28,7 @@ func newPolicyCmd() *cobra.Command { } cmd.AddCommand(newPolicyPublishCmd()) + cmd.AddCommand(newPolicyApplyCmd()) return cmd } diff --git a/cmd/policy_apply.go b/cmd/policy_apply.go new file mode 100644 index 000000000..7fadd3677 --- /dev/null +++ b/cmd/policy_apply.go @@ -0,0 +1,57 @@ +// Copyright 2016-2018, Pulumi Corporation. +// +// 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 cmd + +import ( + "strconv" + + "github.com/pkg/errors" + "github.com/pulumi/pulumi/pkg/backend" + "github.com/pulumi/pulumi/pkg/util/cmdutil" + "github.com/spf13/cobra" +) + +func newPolicyApplyCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "apply / ", + Args: cmdutil.ExactArgs(2), + Short: "Apply a set of policies to a Pulumi organization", + Long: "Apply a set of policies to a Pulumi organization", + Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error { + // + // Obtain current PolicyPack, tied to the Pulumi service backend. + // + + policyPack, err := requirePolicyPack(args[0]) + if err != nil { + return err + } + + version, err := strconv.Atoi(args[1]) + if err != nil { + return errors.Wrapf(err, "Could not parse version (should be an integer)") + } + + // + // Attempt to publish the PolicyPack. + // + + return policyPack.Apply(commandContext(), backend.ApplyOperation{ + Version: version, Scopes: cancellationScopes}) + }), + } + + return cmd +} diff --git a/pkg/apitype/policy.go b/pkg/apitype/policy.go index b799af7e3..fa6119df8 100644 --- a/pkg/apitype/policy.go +++ b/pkg/apitype/policy.go @@ -92,3 +92,9 @@ type GetPolicyPackResponse struct { Version int `json:"version"` Policies []Policy `json:"policies"` } + +// ApplyPolicyPackRequest is the request to apply a Policy Pack to an organization. +type ApplyPolicyPackRequest struct { + Name string `json:"name"` + Version int `json:"version"` +} diff --git a/pkg/backend/httpstate/client/client.go b/pkg/backend/httpstate/client/client.go index e1ca7da95..5bc1c6bc7 100644 --- a/pkg/backend/httpstate/client/client.go +++ b/pkg/backend/httpstate/client/client.go @@ -96,6 +96,12 @@ func publishPolicyPackPath(orgName string) string { return fmt.Sprintf("/api/orgs/%s/policypacks", orgName) } +// appyPolicyPackPath returns the path for an API call to the Pulumi service to apply a PolicyPack +// to a Pulumi organization. +func applyPolicyPackPath(orgName string) string { + return fmt.Sprintf("/api/orgs/%s/policypacks/apply", orgName) +} + // getUpdatePath returns the API path to for the given stack with the given components joined with path separators // and appended to the update root. func getUpdatePath(update UpdateIdentifier, components ...string) string { @@ -463,6 +469,21 @@ func (pc *Client) PublishPolicyPack(ctx context.Context, orgName string, return nil } +// ApplyPolicyPack applies a `PolicyPack` to the Pulumi organization. +func (pc *Client) ApplyPolicyPack(ctx context.Context, orgName string, policyPackName string, + version int) error { + + // TODO: Figure out why the name is being passed in weirdly. + req := apitype.ApplyPolicyPackRequest{Name: "k8s-sec-rules", Version: version} + + err := pc.restCall(ctx, "POST", applyPolicyPackPath(orgName), nil, req, nil) + if err != nil { + return errors.Wrapf(err, "HTTP POST to apply policy pack failed") + } + + return nil +} + // GetUpdateEvents returns all events, taking an optional continuation token from a previous call. func (pc *Client) GetUpdateEvents(ctx context.Context, update UpdateIdentifier, continuationToken *string) (apitype.UpdateResults, error) { diff --git a/pkg/backend/httpstate/policypack.go b/pkg/backend/httpstate/policypack.go index d72f64362..ddaa3a96c 100644 --- a/pkg/backend/httpstate/policypack.go +++ b/pkg/backend/httpstate/policypack.go @@ -89,3 +89,7 @@ func (pack *cloudPolicyPack) Publish( return nil } + +func (pack *cloudPolicyPack) Apply(ctx context.Context, op backend.ApplyOperation) error { + return pack.cl.ApplyPolicyPack(ctx, pack.ref.orgName, string(pack.ref.name), op.Version) +} diff --git a/pkg/backend/policypack.go b/pkg/backend/policypack.go index 71779f5e8..f24bc73f1 100644 --- a/pkg/backend/policypack.go +++ b/pkg/backend/policypack.go @@ -28,6 +28,12 @@ type PublishOperation struct { Scopes CancellationScopeSource } +// ApplyOperation publishes a PolicyPack to the backend. +type ApplyOperation struct { + Version int + Scopes CancellationScopeSource +} + // PolicyPack is a set of policies associated with a particular backend implementation. type PolicyPack interface { // Ref returns a reference to this PolicyPack. @@ -36,4 +42,6 @@ type PolicyPack interface { Backend() Backend // Publish the PolicyPack to the service. Publish(ctx context.Context, op PublishOperation) result.Result + // Apply the PolicyPack to an organization. + Apply(ctx context.Context, op ApplyOperation) error }