Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RabbitMQ provider #269

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ A CLI tool that generates `tf` and `tfstate` files based on existing infrastruct
* [OpenStack](#use-with-openstack)
* Infrastructure Software
* [Kubernetes](#use-with-kubernetes)
* [RabbitMQ](#use-with-rabbitmq)
* Network
* [Cloudflare](#use-with-cloudflare)
* VCS
Expand Down Expand Up @@ -746,6 +747,36 @@ All Kubernetes resources that are currently supported by the Kubernetes provider
* Because Terraform flatmap uses "." to detect the keys for unflattening the maps, some keys with "." in their names are being considered as the maps.
* Since the library assumes empty strings to be empty values (not "0"), there are some issues with optional integer keys that are restricted to be positive.

### Use with RabbitMQ

Example:

```
export RABBITMQ_SERVER_URL=http://foo.bar.localdomain:15672
export RABBITMQ_USERNAME=[RABBITMQ_USERNAME]
export RABBITMQ_PASSWORD=[RABBITMQ_PASSWORD]

terraformer import rabbitmq --resources=vhosts,queues,exchanges
trois-six marked this conversation as resolved.
Show resolved Hide resolved
terraformer import rabbitmq --resources=vhosts,queues,exchanges --filter=rabbitmq_vhost=name1:name2:name3
```

All RabbitMQ resources that are currently supported by the RabbitMQ provider, are also supported by this module. Here is the list of resources which are currently supported by RabbitMQ provider v.1.1.0:

* `bindings`
* `rabbitmq_binding`
* `exchanges`
* `rabbitmq_exchange`
* `permissions`
* `rabbitmq_permissions`
* `policies`
* `rabbitmq_policy`
* `queues`
* `rabbitmq_queue`
* `users`
* `rabbitmq_user`
* `vhosts`
* `rabbitmq_vhost`

### Use with Cloudflare

Example:
Expand Down
64 changes: 64 additions & 0 deletions cmd/provider_cmd_rabbitmq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2018 The Terraformer Authors.
//
// 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 (
"os"

rabbitmq_terraforming "github.com/GoogleCloudPlatform/terraformer/providers/rabbitmq"

"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
"github.com/spf13/cobra"
)

const (
defaultRabbitMQEndpoint = "http://localhost:15672"
)

func newCmdRabbitMQImporter(options ImportOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "rabbitmq",
Short: "Import current state to Terraform configuration from RabbitMQ",
Long: "Import current state to Terraform configuration from RabbitMQ",
RunE: func(cmd *cobra.Command, args []string) error {
endpoint := os.Getenv("RABBITMQ_SERVER_URL")
if len(endpoint) == 0 {
endpoint = defaultRabbitMQEndpoint
}
username := os.Getenv("RABBITMQ_USERNAME")
password := os.Getenv("RABBITMQ_PASSWORD")
provider := newRabbitMQProvider()
err := Import(provider, options, []string{endpoint, username, password})
if err != nil {
return err
}
return nil
},
}

cmd.AddCommand(listCmd(newRabbitMQProvider()))
cmd.PersistentFlags().BoolVarP(&options.Connect, "connect", "c", true, "")
cmd.PersistentFlags().StringSliceVarP(&options.Resources, "resources", "r", []string{}, "vhosts")
cmd.PersistentFlags().StringVarP(&options.PathPattern, "path-pattern", "p", DefaultPathPattern, "{output}/{provider}/custom/{service}/")
cmd.PersistentFlags().StringVarP(&options.PathOutput, "path-output", "o", DefaultPathOutput, "")
cmd.PersistentFlags().StringVarP(&options.State, "state", "s", DefaultState, "local or bucket")
cmd.PersistentFlags().StringVarP(&options.Bucket, "bucket", "b", "", "gs://terraform-state")
cmd.PersistentFlags().StringSliceVarP(&options.Filter, "filter", "f", []string{}, "rabbitmq_type=id1:id2:id4")
return cmd
}

func newRabbitMQProvider() terraform_utils.ProviderGenerator {
return &rabbitmq_terraforming.RBTProvider{}
}
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func providerImporterSubcommands() []func(options ImportOptions) *cobra.Command
newCmdOpenStackImporter,
// Infrastructure Software
newCmdKubernetesImporter,
newCmdRabbitMQImporter,
// Network
newCmdCloudflareImporter,
// VCS
Expand Down Expand Up @@ -77,6 +78,7 @@ func providerGenerators() map[string]func() terraform_utils.ProviderGenerator {
newOpenStackProvider,
// Infrastructure Software
newKubernetesProvider,
newRabbitMQProvider,
// Network
newCloudflareProvider,
// VCS
Expand Down
75 changes: 75 additions & 0 deletions providers/rabbitmq/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2018 The Terraformer Authors.
//
// 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 rabbitmq

import (
"encoding/json"
"fmt"

"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
)

type BindingGenerator struct {
RBTService
}

type Binding struct {
Source string `json:"source"`
Vhost string `json:"vhost"`
Destination string `json:"destination"`
DestinationType string `json:"destination_type"`
PropertiesKey string `json:"properties_key"`
}

type Bindings []Binding

var BindingAllowEmptyValues = []string{"source"}
var BindingAdditionalFields = map[string]interface{}{}

func (g BindingGenerator) createResources(bindings Bindings) []terraform_utils.Resource {
var resources []terraform_utils.Resource
for _, binding := range bindings {
resources = append(resources, terraform_utils.NewResource(
fmt.Sprintf("%s/%s/%s/%s/%s", binding.Vhost, binding.Source, binding.Destination, binding.DestinationType, binding.PropertiesKey),
fmt.Sprintf("binding_%s_%s_%s_%s", normalizeResourceName(binding.Source), normalizeResourceName(binding.Vhost), normalizeResourceName(binding.Destination), normalizeResourceName(binding.DestinationType)),
"rabbitmq_binding",
"rabbitmq",
map[string]string{
"source": binding.Source,
"vhost": binding.Vhost,
"destination": binding.Destination,
"destination_type": binding.DestinationType,
"properties_key": binding.PropertiesKey,
},
BindingAllowEmptyValues,
BindingAdditionalFields,
))
}
return resources
}

func (g *BindingGenerator) InitResources() error {
body, err := g.generateRequest("/api/bindings?columns=source,vhost,destination,destination_type,properties_key")
if err != nil {
return err
}
var bindings Bindings
err = json.Unmarshal([]byte(body), &bindings)
if err != nil {
return err
}
g.Resources = g.createResources(bindings)
return nil
}
72 changes: 72 additions & 0 deletions providers/rabbitmq/exchange.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2018 The Terraformer Authors.
//
// 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 rabbitmq

import (
"encoding/json"
"fmt"

"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
)

type ExchangeGenerator struct {
RBTService
}

type Exchange struct {
Name string `json:"name"`
Vhost string `json:"vhost"`
}

type Exchanges []Exchange

var ExchangeAllowEmptyValues = []string{}
var ExchangeAdditionalFields = map[string]interface{}{}

func (g ExchangeGenerator) createResources(exchanges Exchanges) []terraform_utils.Resource {
var resources []terraform_utils.Resource
for _, exchange := range exchanges {
if len(exchange.Name) == 0 {
continue
}
resources = append(resources, terraform_utils.NewResource(
fmt.Sprintf("%s@%s", exchange.Name, exchange.Vhost),
fmt.Sprintf("exchange_%s_%s", normalizeResourceName(exchange.Vhost), normalizeResourceName(exchange.Name)),
"rabbitmq_exchange",
"rabbitmq",
map[string]string{
"name": exchange.Name,
"vhost": exchange.Vhost,
},
ExchangeAllowEmptyValues,
ExchangeAdditionalFields,
))
}
return resources
}

func (g *ExchangeGenerator) InitResources() error {
body, err := g.generateRequest("/api/exchanges?columns=name,vhost")
if err != nil {
return err
}
var exchanges Exchanges
err = json.Unmarshal([]byte(body), &exchanges)
if err != nil {
return err
}
g.Resources = g.createResources(exchanges)
return nil
}
69 changes: 69 additions & 0 deletions providers/rabbitmq/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2018 The Terraformer Authors.
//
// 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 rabbitmq

import (
"strings"
"unicode"

"golang.org/x/text/secure/precis"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)

func normalizeResourceName(s string) string {
normalize := precis.NewIdentifier(
precis.AdditionalMapping(func() transform.Transformer {
return transform.Chain(norm.NFD, transform.RemoveFunc(func(r rune) bool {
return unicode.Is(unicode.Mn, r)
}))
}),
precis.Norm(norm.NFC),
)
normalizedLower, _ := normalize.String(strings.ToLower(s))
r := strings.NewReplacer(" ", "_",
"!", "_",
"\"", "_",
"#", "_",
"%", "_",
"&", "_",
"'", "_",
"(", "_",
")", "_",
"{", "_",
"}", "_",
"*", "_",
"+", "_",
",", "_",
"-", "_",
".", "_",
"/", "slash",
"|", "_",
"\\", "_",
":", "_",
";", "_",
">", "_",
"=", "_",
"<", "_",
"?", "_",
"[", "_",
"]", "_",
"^", "_",
"`", "_",
"~", "_",
"$", "_",
"@", "_at_")
return r.Replace(normalizedLower)
}