github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/alicloud/resource_alicloud_instance.go (about)

     1  package alicloud
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"github.com/denverdino/aliyungo/common"
    10  	"github.com/denverdino/aliyungo/ecs"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  	"strings"
    14  	"time"
    15  )
    16  
    17  func resourceAliyunInstance() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAliyunInstanceCreate,
    20  		Read:   resourceAliyunInstanceRead,
    21  		Update: resourceAliyunInstanceUpdate,
    22  		Delete: resourceAliyunInstanceDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"availability_zone": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Optional: true,
    28  				ForceNew: true,
    29  				Computed: true,
    30  			},
    31  
    32  			"image_id": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Required: true,
    35  			},
    36  
    37  			"instance_type": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Required: true,
    40  			},
    41  
    42  			"security_groups": &schema.Schema{
    43  				Type:     schema.TypeSet,
    44  				Elem:     &schema.Schema{Type: schema.TypeString},
    45  				Optional: true,
    46  			},
    47  
    48  			"allocate_public_ip": &schema.Schema{
    49  				Type:     schema.TypeBool,
    50  				Optional: true,
    51  				Default:  false,
    52  			},
    53  
    54  			"instance_name": &schema.Schema{
    55  				Type:         schema.TypeString,
    56  				Optional:     true,
    57  				Default:      "ECS-Instance",
    58  				ValidateFunc: validateInstanceName,
    59  			},
    60  
    61  			"description": &schema.Schema{
    62  				Type:         schema.TypeString,
    63  				Optional:     true,
    64  				ValidateFunc: validateInstanceDescription,
    65  			},
    66  
    67  			"internet_charge_type": &schema.Schema{
    68  				Type:         schema.TypeString,
    69  				Optional:     true,
    70  				ForceNew:     true,
    71  				ValidateFunc: validateInternetChargeType,
    72  			},
    73  			"internet_max_bandwidth_in": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Optional: true,
    76  				ForceNew: true,
    77  			},
    78  			"internet_max_bandwidth_out": &schema.Schema{
    79  				Type:         schema.TypeInt,
    80  				Optional:     true,
    81  				ForceNew:     true,
    82  				ValidateFunc: validateInternetMaxBandWidthOut,
    83  			},
    84  			"host_name": &schema.Schema{
    85  				Type:     schema.TypeString,
    86  				Optional: true,
    87  				Computed: true,
    88  			},
    89  			"password": &schema.Schema{
    90  				Type:      schema.TypeString,
    91  				Optional:  true,
    92  				Sensitive: true,
    93  			},
    94  			"io_optimized": &schema.Schema{
    95  				Type:         schema.TypeString,
    96  				Required:     true,
    97  				ForceNew:     true,
    98  				ValidateFunc: validateIoOptimized,
    99  			},
   100  
   101  			"system_disk_category": &schema.Schema{
   102  				Type:     schema.TypeString,
   103  				Default:  "cloud",
   104  				Optional: true,
   105  				ForceNew: true,
   106  				ValidateFunc: validateAllowedStringValue([]string{
   107  					string(ecs.DiskCategoryCloud),
   108  					string(ecs.DiskCategoryCloudSSD),
   109  					string(ecs.DiskCategoryCloudEfficiency),
   110  					string(ecs.DiskCategoryEphemeralSSD),
   111  				}),
   112  			},
   113  			"system_disk_size": &schema.Schema{
   114  				Type:         schema.TypeInt,
   115  				Optional:     true,
   116  				Computed:     true,
   117  				ForceNew:     true,
   118  				ValidateFunc: validateIntegerInRange(40, 500),
   119  			},
   120  
   121  			//subnet_id and vswitch_id both exists, cause compatible old version, and aws habit.
   122  			"subnet_id": &schema.Schema{
   123  				Type:     schema.TypeString,
   124  				Optional: true,
   125  				ForceNew: true,
   126  				Computed: true, //add this schema cause subnet_id not used enter parameter, will different, so will be ForceNew
   127  			},
   128  
   129  			"vswitch_id": &schema.Schema{
   130  				Type:     schema.TypeString,
   131  				Optional: true,
   132  				ForceNew: true,
   133  			},
   134  
   135  			"instance_charge_type": &schema.Schema{
   136  				Type:         schema.TypeString,
   137  				Optional:     true,
   138  				ForceNew:     true,
   139  				ValidateFunc: validateInstanceChargeType,
   140  			},
   141  			"period": &schema.Schema{
   142  				Type:     schema.TypeInt,
   143  				Optional: true,
   144  				ForceNew: true,
   145  			},
   146  
   147  			"public_ip": &schema.Schema{
   148  				Type:     schema.TypeString,
   149  				Optional: true,
   150  				Computed: true,
   151  			},
   152  
   153  			"private_ip": &schema.Schema{
   154  				Type:     schema.TypeString,
   155  				Computed: true,
   156  			},
   157  
   158  			"status": &schema.Schema{
   159  				Type:     schema.TypeString,
   160  				Computed: true,
   161  			},
   162  
   163  			"user_data": &schema.Schema{
   164  				Type:     schema.TypeString,
   165  				Optional: true,
   166  				ForceNew: true,
   167  			},
   168  
   169  			"tags": tagsSchema(),
   170  		},
   171  	}
   172  }
   173  
   174  func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   175  	conn := meta.(*AliyunClient).ecsconn
   176  
   177  	// create postpaid instance by runInstances API
   178  	if v := d.Get("instance_charge_type").(string); v != string(common.PrePaid) {
   179  		return resourceAliyunRunInstance(d, meta)
   180  	}
   181  
   182  	args, err := buildAliyunInstanceArgs(d, meta)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	instanceID, err := conn.CreateInstance(args)
   188  	if err != nil {
   189  		return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err)
   190  	}
   191  
   192  	d.SetId(instanceID)
   193  
   194  	d.Set("password", d.Get("password"))
   195  	//d.Set("system_disk_category", d.Get("system_disk_category"))
   196  	//d.Set("system_disk_size", d.Get("system_disk_size"))
   197  
   198  	if err := allocateIpAndBandWidthRelative(d, meta); err != nil {
   199  		return fmt.Errorf("allocateIpAndBandWidthRelative err: %#v", err)
   200  	}
   201  
   202  	// after instance created, its status is pending,
   203  	// so we need to wait it become to stopped and then start it
   204  	if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil {
   205  		log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Stopped, err)
   206  	}
   207  
   208  	if err := conn.StartInstance(d.Id()); err != nil {
   209  		return fmt.Errorf("Start instance got error: %#v", err)
   210  	}
   211  
   212  	if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil {
   213  		log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err)
   214  	}
   215  
   216  	return resourceAliyunInstanceUpdate(d, meta)
   217  }
   218  
   219  func resourceAliyunRunInstance(d *schema.ResourceData, meta interface{}) error {
   220  	conn := meta.(*AliyunClient).ecsconn
   221  	newConn := meta.(*AliyunClient).ecsNewconn
   222  
   223  	args, err := buildAliyunInstanceArgs(d, meta)
   224  	if err != nil {
   225  		return err
   226  	}
   227  
   228  	if args.IoOptimized == "optimized" {
   229  		args.IoOptimized = ecs.IoOptimized("true")
   230  	} else {
   231  		args.IoOptimized = ecs.IoOptimized("false")
   232  	}
   233  
   234  	runArgs, err := buildAliyunRunInstancesArgs(d, meta)
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	runArgs.CreateInstanceArgs = *args
   240  
   241  	// runInstances is support in version 2016-03-14
   242  	instanceIds, err := newConn.RunInstances(runArgs)
   243  
   244  	if err != nil {
   245  		return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err)
   246  	}
   247  
   248  	d.SetId(instanceIds[0])
   249  
   250  	d.Set("password", d.Get("password"))
   251  	d.Set("system_disk_category", d.Get("system_disk_category"))
   252  	d.Set("system_disk_size", d.Get("system_disk_size"))
   253  
   254  	// after instance created, its status change from pending, starting to running
   255  	if err := conn.WaitForInstanceAsyn(d.Id(), ecs.Running, defaultTimeout); err != nil {
   256  		log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err)
   257  	}
   258  
   259  	if err := allocateIpAndBandWidthRelative(d, meta); err != nil {
   260  		return fmt.Errorf("allocateIpAndBandWidthRelative err: %#v", err)
   261  	}
   262  
   263  	if err := conn.WaitForInstanceAsyn(d.Id(), ecs.Running, defaultTimeout); err != nil {
   264  		log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err)
   265  	}
   266  
   267  	return resourceAliyunInstanceUpdate(d, meta)
   268  }
   269  
   270  func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error {
   271  	client := meta.(*AliyunClient)
   272  	conn := client.ecsconn
   273  
   274  	instance, err := client.QueryInstancesById(d.Id())
   275  
   276  	if err != nil {
   277  		if notFoundError(err) {
   278  			d.SetId("")
   279  			return nil
   280  		}
   281  		return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err)
   282  	}
   283  
   284  	disk, diskErr := client.QueryInstanceSystemDisk(d.Id())
   285  
   286  	if diskErr != nil {
   287  		if notFoundError(diskErr) {
   288  			d.SetId("")
   289  			return nil
   290  		}
   291  		return fmt.Errorf("Error DescribeSystemDisk: %#v", err)
   292  	}
   293  
   294  	d.Set("instance_name", instance.InstanceName)
   295  	d.Set("description", instance.Description)
   296  	d.Set("status", instance.Status)
   297  	d.Set("availability_zone", instance.ZoneId)
   298  	d.Set("host_name", instance.HostName)
   299  	d.Set("image_id", instance.ImageId)
   300  	d.Set("instance_type", instance.InstanceType)
   301  	d.Set("system_disk_category", disk.Category)
   302  	d.Set("system_disk_size", disk.Size)
   303  
   304  	// In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'.
   305  	// In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'.
   306  	d.Set("internet_charge_type", instance.InternetChargeType)
   307  
   308  	if d.Get("allocate_public_ip").(bool) {
   309  		d.Set("public_ip", instance.PublicIpAddress.IpAddress[0])
   310  	}
   311  
   312  	if ecs.StringOrBool(instance.IoOptimized).Value {
   313  		d.Set("io_optimized", "optimized")
   314  	} else {
   315  		d.Set("io_optimized", "none")
   316  	}
   317  
   318  	if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" {
   319  		ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
   320  		d.Set("private_ip", ipAddress)
   321  		d.Set("subnet_id", instance.VpcAttributes.VSwitchId)
   322  		d.Set("vswitch_id", instance.VpcAttributes.VSwitchId)
   323  	} else {
   324  		ipAddress := strings.Join(ecs.IpAddressSetType(instance.InnerIpAddress).IpAddress, ",")
   325  		d.Set("private_ip", ipAddress)
   326  	}
   327  
   328  	if d.Get("user_data").(string) != "" {
   329  		ud, err := conn.DescribeUserdata(&ecs.DescribeUserdataArgs{
   330  			RegionId:   getRegion(d, meta),
   331  			InstanceId: d.Id(),
   332  		})
   333  
   334  		if err != nil {
   335  			log.Printf("[ERROR] DescribeUserData for instance got error: %#v", err)
   336  		}
   337  		d.Set("user_data", userDataHashSum(ud.UserData))
   338  	}
   339  
   340  	tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{
   341  		RegionId:     getRegion(d, meta),
   342  		ResourceType: ecs.TagResourceInstance,
   343  		ResourceId:   d.Id(),
   344  	})
   345  
   346  	if err != nil {
   347  		log.Printf("[ERROR] DescribeTags for instance got error: %#v", err)
   348  	}
   349  	d.Set("tags", tagsToMap(tags))
   350  
   351  	return nil
   352  }
   353  
   354  func resourceAliyunInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   355  
   356  	client := meta.(*AliyunClient)
   357  	conn := client.ecsconn
   358  
   359  	d.Partial(true)
   360  
   361  	if err := setTags(client, ecs.TagResourceInstance, d); err != nil {
   362  		log.Printf("[DEBUG] Set tags for instance got error: %#v", err)
   363  		return fmt.Errorf("Set tags for instance got error: %#v", err)
   364  	} else {
   365  		d.SetPartial("tags")
   366  	}
   367  
   368  	attributeUpdate := false
   369  	args := &ecs.ModifyInstanceAttributeArgs{
   370  		InstanceId: d.Id(),
   371  	}
   372  
   373  	if d.HasChange("instance_name") {
   374  		log.Printf("[DEBUG] ModifyInstanceAttribute instance_name")
   375  		d.SetPartial("instance_name")
   376  		args.InstanceName = d.Get("instance_name").(string)
   377  
   378  		attributeUpdate = true
   379  	}
   380  
   381  	if d.HasChange("description") {
   382  		log.Printf("[DEBUG] ModifyInstanceAttribute description")
   383  		d.SetPartial("description")
   384  		args.Description = d.Get("description").(string)
   385  
   386  		attributeUpdate = true
   387  	}
   388  
   389  	if d.HasChange("host_name") {
   390  		log.Printf("[DEBUG] ModifyInstanceAttribute host_name")
   391  		d.SetPartial("host_name")
   392  		args.HostName = d.Get("host_name").(string)
   393  
   394  		attributeUpdate = true
   395  	}
   396  
   397  	passwordUpdate := false
   398  	if d.HasChange("password") {
   399  		log.Printf("[DEBUG] ModifyInstanceAttribute password")
   400  		d.SetPartial("password")
   401  		args.Password = d.Get("password").(string)
   402  
   403  		attributeUpdate = true
   404  		passwordUpdate = true
   405  	}
   406  
   407  	if attributeUpdate {
   408  		if err := conn.ModifyInstanceAttribute(args); err != nil {
   409  			return fmt.Errorf("Modify instance attribute got error: %#v", err)
   410  		}
   411  	}
   412  
   413  	if passwordUpdate {
   414  		if v, ok := d.GetOk("status"); ok && v.(string) != "" {
   415  			if ecs.InstanceStatus(d.Get("status").(string)) == ecs.Running {
   416  				log.Printf("[DEBUG] RebootInstance after change password")
   417  				if err := conn.RebootInstance(d.Id(), false); err != nil {
   418  					return fmt.Errorf("RebootInstance got error: %#v", err)
   419  				}
   420  
   421  				if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil {
   422  					return fmt.Errorf("WaitForInstance got error: %#v", err)
   423  				}
   424  			}
   425  		}
   426  	}
   427  
   428  	if d.HasChange("security_groups") {
   429  		o, n := d.GetChange("security_groups")
   430  		os := o.(*schema.Set)
   431  		ns := n.(*schema.Set)
   432  
   433  		rl := expandStringList(os.Difference(ns).List())
   434  		al := expandStringList(ns.Difference(os).List())
   435  
   436  		if len(al) > 0 {
   437  			err := client.JoinSecurityGroups(d.Id(), al)
   438  			if err != nil {
   439  				return err
   440  			}
   441  		}
   442  		if len(rl) > 0 {
   443  			err := client.LeaveSecurityGroups(d.Id(), rl)
   444  			if err != nil {
   445  				return err
   446  			}
   447  		}
   448  
   449  		d.SetPartial("security_groups")
   450  	}
   451  
   452  	d.Partial(false)
   453  	return resourceAliyunInstanceRead(d, meta)
   454  }
   455  
   456  func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   457  	client := meta.(*AliyunClient)
   458  	conn := client.ecsconn
   459  
   460  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   461  		instance, err := client.QueryInstancesById(d.Id())
   462  		if err != nil {
   463  			if notFoundError(err) {
   464  				return nil
   465  			}
   466  		}
   467  
   468  		if instance.Status != ecs.Stopped {
   469  			if err := conn.StopInstance(d.Id(), true); err != nil {
   470  				return resource.RetryableError(fmt.Errorf("ECS stop error - trying again."))
   471  			}
   472  
   473  			if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil {
   474  				return resource.RetryableError(fmt.Errorf("Waiting for ecs stopped timeout - trying again."))
   475  			}
   476  		}
   477  
   478  		if err := conn.DeleteInstance(d.Id()); err != nil {
   479  			return resource.RetryableError(fmt.Errorf("ECS Instance in use - trying again while it is deleted."))
   480  		}
   481  
   482  		return nil
   483  	})
   484  
   485  }
   486  
   487  func allocateIpAndBandWidthRelative(d *schema.ResourceData, meta interface{}) error {
   488  	conn := meta.(*AliyunClient).ecsconn
   489  	if d.Get("allocate_public_ip").(bool) {
   490  		if d.Get("internet_max_bandwidth_out") == 0 {
   491  			return fmt.Errorf("Error: if allocate_public_ip is true than the internet_max_bandwidth_out cannot equal zero.")
   492  		}
   493  		_, err := conn.AllocatePublicIpAddress(d.Id())
   494  		if err != nil {
   495  			return fmt.Errorf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err)
   496  		}
   497  	}
   498  	return nil
   499  }
   500  
   501  func buildAliyunRunInstancesArgs(d *schema.ResourceData, meta interface{}) (*ecs.RunInstanceArgs, error) {
   502  	args := &ecs.RunInstanceArgs{
   503  		MaxAmount: DEFAULT_INSTANCE_COUNT,
   504  		MinAmount: DEFAULT_INSTANCE_COUNT,
   505  	}
   506  
   507  	bussStr, err := json.Marshal(DefaultBusinessInfo)
   508  	if err != nil {
   509  		log.Printf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo)
   510  	}
   511  
   512  	args.BusinessInfo = string(bussStr)
   513  
   514  	subnetValue := d.Get("subnet_id").(string)
   515  	vswitchValue := d.Get("vswitch_id").(string)
   516  	//networkValue := d.Get("instance_network_type").(string)
   517  
   518  	// because runInstance is not compatible with createInstance, force NetworkType value to classic
   519  	if subnetValue == "" && vswitchValue == "" {
   520  		args.NetworkType = string(ClassicNet)
   521  	}
   522  
   523  	return args, nil
   524  }
   525  
   526  func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) {
   527  	client := meta.(*AliyunClient)
   528  
   529  	args := &ecs.CreateInstanceArgs{
   530  		RegionId:     getRegion(d, meta),
   531  		InstanceType: d.Get("instance_type").(string),
   532  	}
   533  
   534  	imageID := d.Get("image_id").(string)
   535  
   536  	args.ImageId = imageID
   537  
   538  	systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string))
   539  	systemDiskSize := d.Get("system_disk_size").(int)
   540  
   541  	zoneID := d.Get("availability_zone").(string)
   542  	// check instanceType and systemDiskCategory, when zoneID is not empty
   543  	if zoneID != "" {
   544  		zone, err := client.DescribeZone(zoneID)
   545  		if err != nil {
   546  			return nil, err
   547  		}
   548  
   549  		if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil {
   550  			return nil, err
   551  		}
   552  
   553  		if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
   554  			return nil, err
   555  		}
   556  
   557  		args.ZoneId = zoneID
   558  
   559  	}
   560  
   561  	args.SystemDisk = ecs.SystemDiskType{
   562  		Category: systemDiskCategory,
   563  		Size:     systemDiskSize,
   564  	}
   565  
   566  	sgs, ok := d.GetOk("security_groups")
   567  
   568  	if ok {
   569  		sgList := expandStringList(sgs.(*schema.Set).List())
   570  		sg0 := sgList[0]
   571  		// check security group instance exist
   572  		_, err := client.DescribeSecurity(sg0)
   573  		if err == nil {
   574  			args.SecurityGroupId = sg0
   575  		}
   576  	}
   577  
   578  	if v := d.Get("instance_name").(string); v != "" {
   579  		args.InstanceName = v
   580  	}
   581  
   582  	if v := d.Get("description").(string); v != "" {
   583  		args.Description = v
   584  	}
   585  
   586  	if v := d.Get("internet_charge_type").(string); v != "" {
   587  		args.InternetChargeType = common.InternetChargeType(v)
   588  	}
   589  
   590  	if v := d.Get("internet_max_bandwidth_out").(int); v != 0 {
   591  		args.InternetMaxBandwidthOut = v
   592  	}
   593  
   594  	if v := d.Get("host_name").(string); v != "" {
   595  		args.HostName = v
   596  	}
   597  
   598  	if v := d.Get("password").(string); v != "" {
   599  		args.Password = v
   600  	}
   601  
   602  	if v := d.Get("io_optimized").(string); v != "" {
   603  		args.IoOptimized = ecs.IoOptimized(v)
   604  	}
   605  
   606  	vswitchValue := d.Get("subnet_id").(string)
   607  	if vswitchValue == "" {
   608  		vswitchValue = d.Get("vswitch_id").(string)
   609  	}
   610  	if vswitchValue != "" {
   611  		args.VSwitchId = vswitchValue
   612  		if d.Get("allocate_public_ip").(bool) && args.InternetMaxBandwidthOut <= 0 {
   613  			return nil, fmt.Errorf("Invalid internet_max_bandwidth_out result in allocation public ip failed in the VPC.")
   614  		}
   615  	}
   616  
   617  	if v := d.Get("instance_charge_type").(string); v != "" {
   618  		args.InstanceChargeType = common.InstanceChargeType(v)
   619  	}
   620  
   621  	log.Printf("[DEBUG] period is %d", d.Get("period").(int))
   622  	if v := d.Get("period").(int); v != 0 {
   623  		args.Period = v
   624  	} else if args.InstanceChargeType == common.PrePaid {
   625  		return nil, fmt.Errorf("period is required for instance_charge_type is PrePaid")
   626  	}
   627  
   628  	if v := d.Get("user_data").(string); v != "" {
   629  		args.UserData = v
   630  	}
   631  
   632  	return args, nil
   633  }
   634  
   635  func userDataHashSum(user_data string) string {
   636  	// Check whether the user_data is not Base64 encoded.
   637  	// Always calculate hash of base64 decoded value since we
   638  	// check against double-encoding when setting it
   639  	v, base64DecodeError := base64.StdEncoding.DecodeString(user_data)
   640  	if base64DecodeError != nil {
   641  		v = []byte(user_data)
   642  	}
   643  	return string(v)
   644  }