github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/rundeck/resource_project.go (about)

     1  package rundeck
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  
    11  	"github.com/apparentlymart/go-rundeck-api/rundeck"
    12  )
    13  
    14  var projectConfigAttributes = map[string]string{
    15  	"project.name":                          "name",
    16  	"project.description":                   "description",
    17  	"service.FileCopier.default.provider":   "default_node_file_copier_plugin",
    18  	"service.NodeExecutor.default.provider": "default_node_executor_plugin",
    19  	"project.ssh-authentication":            "ssh_authentication_type",
    20  	"project.ssh-key-storage-path":          "ssh_key_storage_path",
    21  	"project.ssh-keypath":                   "ssh_key_file_path",
    22  }
    23  
    24  func resourceRundeckProject() *schema.Resource {
    25  	return &schema.Resource{
    26  		Create: CreateProject,
    27  		Update: UpdateProject,
    28  		Delete: DeleteProject,
    29  		Exists: ProjectExists,
    30  		Read:   ReadProject,
    31  
    32  		Schema: map[string]*schema.Schema{
    33  			"name": &schema.Schema{
    34  				Type:        schema.TypeString,
    35  				Required:    true,
    36  				Description: "Unique name for the project",
    37  				ForceNew:    true,
    38  			},
    39  
    40  			"description": &schema.Schema{
    41  				Type:        schema.TypeString,
    42  				Optional:    true,
    43  				Description: "Description of the project to be shown in the Rundeck UI",
    44  				Default:     "Managed by Terraform",
    45  			},
    46  
    47  			"ui_url": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Required: false,
    50  				Computed: true,
    51  			},
    52  
    53  			"resource_model_source": &schema.Schema{
    54  				Type:     schema.TypeList,
    55  				Required: true,
    56  				Elem: &schema.Resource{
    57  					Schema: map[string]*schema.Schema{
    58  						"type": &schema.Schema{
    59  							Type:        schema.TypeString,
    60  							Required:    true,
    61  							Description: "Name of the resource model plugin to use",
    62  						},
    63  						"config": &schema.Schema{
    64  							Type:        schema.TypeMap,
    65  							Required:    true,
    66  							Description: "Configuration parameters for the selected plugin",
    67  						},
    68  					},
    69  				},
    70  			},
    71  
    72  			"default_node_file_copier_plugin": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Optional: true,
    75  				Default:  "jsch-scp",
    76  			},
    77  
    78  			"default_node_executor_plugin": &schema.Schema{
    79  				Type:     schema.TypeString,
    80  				Optional: true,
    81  				Default:  "jsch-ssh",
    82  			},
    83  
    84  			"ssh_authentication_type": &schema.Schema{
    85  				Type:     schema.TypeString,
    86  				Optional: true,
    87  				Default:  "privateKey",
    88  			},
    89  
    90  			"ssh_key_storage_path": &schema.Schema{
    91  				Type:     schema.TypeString,
    92  				Optional: true,
    93  			},
    94  
    95  			"ssh_key_file_path": &schema.Schema{
    96  				Type:     schema.TypeString,
    97  				Optional: true,
    98  			},
    99  
   100  			"extra_config": &schema.Schema{
   101  				Type:        schema.TypeMap,
   102  				Optional:    true,
   103  				Description: "Additional raw configuration parameters to include in the project configuration, with dots replaced with slashes in the key names due to limitations in Terraform's config language.",
   104  			},
   105  		},
   106  	}
   107  }
   108  
   109  func CreateProject(d *schema.ResourceData, meta interface{}) error {
   110  	client := meta.(*rundeck.Client)
   111  
   112  	// Rundeck's model is a little inconsistent in that we can create
   113  	// a project via a high-level structure but yet we must update
   114  	// the project via its raw config properties.
   115  	// For simplicity's sake we create a bare minimum project here
   116  	// and then delegate to UpdateProject to fill in the rest of the
   117  	// configuration via the raw config properties.
   118  
   119  	project, err := client.CreateProject(&rundeck.Project{
   120  		Name: d.Get("name").(string),
   121  	})
   122  
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	d.SetId(project.Name)
   128  	d.Set("id", project.Name)
   129  
   130  	return UpdateProject(d, meta)
   131  }
   132  
   133  func UpdateProject(d *schema.ResourceData, meta interface{}) error {
   134  	client := meta.(*rundeck.Client)
   135  
   136  	// In Rundeck, updates are always in terms of the low-level config
   137  	// properties map, so we need to transform our data structure
   138  	// into the equivalent raw properties.
   139  
   140  	projectName := d.Id()
   141  
   142  	updateMap := map[string]string{}
   143  
   144  	slashReplacer := strings.NewReplacer("/", ".")
   145  	if extraConfig := d.Get("extra_config"); extraConfig != nil {
   146  		for k, v := range extraConfig.(map[string]interface{}) {
   147  			updateMap[slashReplacer.Replace(k)] = v.(string)
   148  		}
   149  	}
   150  
   151  	for configKey, attrKey := range projectConfigAttributes {
   152  		v := d.Get(attrKey).(string)
   153  		if v != "" {
   154  			updateMap[configKey] = v
   155  		}
   156  	}
   157  
   158  	for i, rmsi := range d.Get("resource_model_source").([]interface{}) {
   159  		rms := rmsi.(map[string]interface{})
   160  		pluginType := rms["type"].(string)
   161  		ci := rms["config"].(map[string]interface{})
   162  		attrKeyPrefix := fmt.Sprintf("resources.source.%v.", i+1)
   163  		typeKey := attrKeyPrefix + "type"
   164  		configKeyPrefix := fmt.Sprintf("%vconfig.", attrKeyPrefix)
   165  		updateMap[typeKey] = pluginType
   166  		for k, v := range ci {
   167  			updateMap[configKeyPrefix+k] = v.(string)
   168  		}
   169  	}
   170  
   171  	err := client.SetProjectConfig(projectName, updateMap)
   172  
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	return ReadProject(d, meta)
   178  }
   179  
   180  func ReadProject(d *schema.ResourceData, meta interface{}) error {
   181  	client := meta.(*rundeck.Client)
   182  
   183  	name := d.Id()
   184  	project, err := client.GetProject(name)
   185  
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	for configKey, attrKey := range projectConfigAttributes {
   191  		d.Set(projectConfigAttributes[configKey], nil)
   192  		if v, ok := project.Config[configKey]; ok {
   193  			d.Set(attrKey, v)
   194  			// Remove this key so it won't get included in extra_config
   195  			// later.
   196  			delete(project.Config, configKey)
   197  		}
   198  	}
   199  
   200  	resourceSourceMap := map[int]interface{}{}
   201  	configMaps := map[int]interface{}{}
   202  	for configKey, v := range project.Config {
   203  		if strings.HasPrefix(configKey, "resources.source.") {
   204  			nameParts := strings.Split(configKey, ".")
   205  
   206  			if len(nameParts) < 4 {
   207  				continue
   208  			}
   209  
   210  			index, err := strconv.Atoi(nameParts[2])
   211  			if err != nil {
   212  				continue
   213  			}
   214  
   215  			if _, ok := resourceSourceMap[index]; !ok {
   216  				configMap := map[string]interface{}{}
   217  				configMaps[index] = configMap
   218  				resourceSourceMap[index] = map[string]interface{}{
   219  					"config": configMap,
   220  				}
   221  			}
   222  
   223  			switch nameParts[3] {
   224  			case "type":
   225  				if len(nameParts) != 4 {
   226  					continue
   227  				}
   228  				m := resourceSourceMap[index].(map[string]interface{})
   229  				m["type"] = v
   230  			case "config":
   231  				if len(nameParts) != 5 {
   232  					continue
   233  				}
   234  				m := configMaps[index].(map[string]interface{})
   235  				m[nameParts[4]] = v
   236  			default:
   237  				continue
   238  			}
   239  
   240  			// Remove this key so it won't get included in extra_config
   241  			// later.
   242  			delete(project.Config, configKey)
   243  		}
   244  	}
   245  
   246  	resourceSources := []map[string]interface{}{}
   247  	resourceSourceIndices := []int{}
   248  	for k := range resourceSourceMap {
   249  		resourceSourceIndices = append(resourceSourceIndices, k)
   250  	}
   251  	sort.Ints(resourceSourceIndices)
   252  
   253  	for _, index := range resourceSourceIndices {
   254  		resourceSources = append(resourceSources, resourceSourceMap[index].(map[string]interface{}))
   255  	}
   256  	d.Set("resource_model_source", resourceSources)
   257  
   258  	extraConfig := map[string]string{}
   259  	dotReplacer := strings.NewReplacer(".", "/")
   260  	for k, v := range project.Config {
   261  		extraConfig[dotReplacer.Replace(k)] = v
   262  	}
   263  	d.Set("extra_config", extraConfig)
   264  
   265  	d.Set("name", project.Name)
   266  	d.Set("ui_url", project.URL)
   267  
   268  	return nil
   269  }
   270  
   271  func ProjectExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   272  	client := meta.(*rundeck.Client)
   273  
   274  	name := d.Id()
   275  	_, err := client.GetProject(name)
   276  
   277  	if _, ok := err.(rundeck.NotFoundError); ok {
   278  		return false, nil
   279  	}
   280  
   281  	if err != nil {
   282  		return false, err
   283  	}
   284  
   285  	return true, nil
   286  }
   287  
   288  func DeleteProject(d *schema.ResourceData, meta interface{}) error {
   289  	client := meta.(*rundeck.Client)
   290  
   291  	name := d.Id()
   292  	return client.DeleteProject(name)
   293  }