<commit_before>package main

import (
	"fmt"
	"github.com/amfranz/go-xen-api-client"
	"github.com/hashicorp/terraform/helper/schema"
)

const (
	vmSchemaNameLabel        = "name_label"
	vmSchemaBaseTemplateName = "base_template_name"
	vmSchemaXenstoreData     = "xenstore_data"
)

func resourceVM() *schema.Resource {
	return &schema.Resource{
		Create: resourceVMCreate,
		Read:   resourceVMRead,
		Update: resourceVMUpdate,
		Delete: resourceVMDelete,
		Exists: resourceVMExists,

		Schema: map[string]*schema.Schema{
			vmSchemaNameLabel: &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			vmSchemaBaseTemplateName: &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			vmSchemaXenstoreData: &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
		},
	}
}

func resourceVMCreate(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	dBaseTemplateName := d.Get(vmSchemaBaseTemplateName).(string)

	xenBaseTemplates, err := c.client.VM().GetByNameLabel(c.session, dBaseTemplateName)
	if err != nil {
		return err
	}

	if len(xenBaseTemplates) == 0 {
		return fmt.Errorf("No template with label %q has been found", dBaseTemplateName)
	}

	if len(xenBaseTemplates) > 1 {
		return fmt.Errorf("More than one template with label %q has been found", dBaseTemplateName)
	}

	xenBaseTemplate := xenBaseTemplates[0]

	isTemplate, err := c.client.VM().GetIsATemplate(c.session, xenBaseTemplate)
	if err != nil {
		return err
	}

	if !isTemplate {
		return fmt.Errorf("A VM with name %q has been found, but it is not a template", dBaseTemplateName)
	}

	dNameLabel := d.Get(vmSchemaNameLabel).(string)

	xenVM, err := c.client.VM().Clone(c.session, xenBaseTemplate, dNameLabel)
	if err != nil {
		return err
	}

	xenUUID, err := c.client.VM().GetUUID(c.session, xenVM)
	if err != nil {
		return err
	}

	d.SetId(xenUUID)

	dXenstoreDataRaw, ok := d.GetOk(vmSchemaXenstoreData)
	if ok {
		dXenstoreData := make(map[string]string)
		for key, value := range dXenstoreDataRaw.(map[string]interface{}) {
			dXenstoreData[key] = value.(string)
		}

		err = c.client.VM().SetXenstoreData(c.session, xenVM, dXenstoreData)
		if err != nil {
			return err
		}
	}

	err = c.client.VM().Provision(c.session, xenVM)
	if err != nil {
		return err
	}

	return nil
}

func resourceVMRead(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	vmRecord, err := c.client.VM().GetRecord(c.session, xenVM)
	if err != nil {
		return err
	}

	d.Set(vmSchemaNameLabel, vmRecord.NameLabel)

	vmBaseTemplateName, ok := vmRecord.OtherConfig["base_template_name"]
	if ok {
		d.Set(vmSchemaBaseTemplateName, vmBaseTemplateName)
	}

	d.Set(vmSchemaXenstoreData, vmRecord.XenstoreData)

	return nil
}

func resourceVMUpdate(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	vmRecord, err := c.client.VM().GetRecord(c.session, xenVM)
	if err != nil {
		return err
	}

	d.Partial(true)

	dNameLabel := d.Get(vmSchemaNameLabel).(string)
	if vmRecord.NameLabel != dNameLabel {
		err := c.client.VM().SetNameLabel(c.session, xenVM, dNameLabel)
		if err != nil {
			return err
		}

		d.SetPartial(vmSchemaNameLabel)
	}

	dXenstoreDataRaw, ok := d.GetOk(vmSchemaXenstoreData)
	if ok {
		dXenstoreData := make(map[string]string)
		for key, value := range dXenstoreDataRaw.(map[string]interface{}) {
			dXenstoreData[key] = value.(string)
		}

		err = c.client.VM().SetXenstoreData(c.session, xenVM, dXenstoreData)
		if err != nil {
			return err
		}

		d.SetPartial(vmSchemaXenstoreData)
	}

	d.Partial(false)

	return resourceVMRead(d, m)
}

func resourceVMDelete(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	err = c.client.VM().Destroy(c.session, xenVM)
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}

func resourceVMExists(d *schema.ResourceData, m interface{}) (bool, error) {
	c := m.(*Connection)

	_, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}
<commit_msg>Improve validation of VM base templates.<commit_after>package main

import (
	"fmt"
	"github.com/amfranz/go-xen-api-client"
	"github.com/hashicorp/terraform/helper/schema"
)

const (
	vmSchemaNameLabel        = "name_label"
	vmSchemaBaseTemplateName = "base_template_name"
	vmSchemaXenstoreData     = "xenstore_data"
)

func resourceVM() *schema.Resource {
	return &schema.Resource{
		Create: resourceVMCreate,
		Read:   resourceVMRead,
		Update: resourceVMUpdate,
		Delete: resourceVMDelete,
		Exists: resourceVMExists,

		Schema: map[string]*schema.Schema{
			vmSchemaNameLabel: &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			vmSchemaBaseTemplateName: &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			vmSchemaXenstoreData: &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
		},
	}
}

func filterVMTemplates(c *Connection, vms []xenAPI.VMRef) ([]xenAPI.VMRef, error) {
	var templates []xenAPI.VMRef
	for _, vm := range vms {
		isATemplate, err := c.client.VM().GetIsATemplate(c.session, vm)
		if err != nil {
			return templates, err
		}
		if isATemplate {
			templates = append(templates, vm)
		}
	}
	return templates, nil
}

func resourceVMCreate(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	dBaseTemplateName := d.Get(vmSchemaBaseTemplateName).(string)

	xenBaseTemplates, err := c.client.VM().GetByNameLabel(c.session, dBaseTemplateName)
	if err != nil {
		return err
	}

	xenBaseTemplates, err = filterVMTemplates(c, xenBaseTemplates)
	if err != nil {
		return err
	}

	if len(xenBaseTemplates) == 0 {
		return fmt.Errorf("No VM template with label %q has been found", dBaseTemplateName)
	}

	if len(xenBaseTemplates) > 1 {
		return fmt.Errorf("More than one VM template with label %q has been found", dBaseTemplateName)
	}

	xenBaseTemplate := xenBaseTemplates[0]

	dNameLabel := d.Get(vmSchemaNameLabel).(string)

	xenVM, err := c.client.VM().Clone(c.session, xenBaseTemplate, dNameLabel)
	if err != nil {
		return err
	}

	xenUUID, err := c.client.VM().GetUUID(c.session, xenVM)
	if err != nil {
		return err
	}

	d.SetId(xenUUID)

	dXenstoreDataRaw, ok := d.GetOk(vmSchemaXenstoreData)
	if ok {
		dXenstoreData := make(map[string]string)
		for key, value := range dXenstoreDataRaw.(map[string]interface{}) {
			dXenstoreData[key] = value.(string)
		}

		err = c.client.VM().SetXenstoreData(c.session, xenVM, dXenstoreData)
		if err != nil {
			return err
		}
	}

	err = c.client.VM().Provision(c.session, xenVM)
	if err != nil {
		return err
	}

	return nil
}

func resourceVMRead(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	vmRecord, err := c.client.VM().GetRecord(c.session, xenVM)
	if err != nil {
		return err
	}

	d.Set(vmSchemaNameLabel, vmRecord.NameLabel)

	vmBaseTemplateName, ok := vmRecord.OtherConfig["base_template_name"]
	if ok {
		d.Set(vmSchemaBaseTemplateName, vmBaseTemplateName)
	}

	d.Set(vmSchemaXenstoreData, vmRecord.XenstoreData)

	return nil
}

func resourceVMUpdate(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	vmRecord, err := c.client.VM().GetRecord(c.session, xenVM)
	if err != nil {
		return err
	}

	d.Partial(true)

	dNameLabel := d.Get(vmSchemaNameLabel).(string)
	if vmRecord.NameLabel != dNameLabel {
		err := c.client.VM().SetNameLabel(c.session, xenVM, dNameLabel)
		if err != nil {
			return err
		}

		d.SetPartial(vmSchemaNameLabel)
	}

	dXenstoreDataRaw, ok := d.GetOk(vmSchemaXenstoreData)
	if ok {
		dXenstoreData := make(map[string]string)
		for key, value := range dXenstoreDataRaw.(map[string]interface{}) {
			dXenstoreData[key] = value.(string)
		}

		err = c.client.VM().SetXenstoreData(c.session, xenVM, dXenstoreData)
		if err != nil {
			return err
		}

		d.SetPartial(vmSchemaXenstoreData)
	}

	d.Partial(false)

	return resourceVMRead(d, m)
}

func resourceVMDelete(d *schema.ResourceData, m interface{}) error {
	c := m.(*Connection)

	xenVM, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				d.SetId("")
				return nil
			}
		}

		return err
	}

	err = c.client.VM().Destroy(c.session, xenVM)
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}

func resourceVMExists(d *schema.ResourceData, m interface{}) (bool, error) {
	c := m.(*Connection)

	_, err := c.client.VM().GetByUUID(c.session, d.Id())
	if err != nil {
		if xenErr, ok := err.(*xenAPI.Error); ok {
			if xenErr.Code() == xenAPI.ERR_UUID_INVALID {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}

{'fraction_non_alphanumeric': 0.1423258618956679, 'fraction_numerical': 0.0006030756859985928, 'mean_word_length': 3.7022684310018903, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5773184', 'n_tokens_mistral': 4087, 'n_tokens_neox': 3725, 'n_words': 783}