Fix get_status api in COS policy manager

GetStatus was deprecated in Update Engine. This broke the get_status api
in COS Policy Manager. Changing the implementation to use
GetStatusAdvanced api instead.

BUG=b/219970525
TEST=presubmit and manually tested on VM
RELEASE_NOTES=Fix get_status api in COS Policy Manager

Change-Id: Ia0d0db2f5552cf528fb0863da1011aa1365a5a34
diff --git a/src/policy_manager/dbus/update_engine_client.go b/src/policy_manager/dbus/update_engine_client.go
index 5b84086..bc0da24 100644
--- a/src/policy_manager/dbus/update_engine_client.go
+++ b/src/policy_manager/dbus/update_engine_client.go
@@ -14,6 +14,8 @@
 
 package dbus
 
+import "policy_manager/policymanagerproto"
+
 // UpdateEngineClient is the generic interface for interacting with update-engine.
 type UpdateEngineClient interface {
 	// SetChannel Sets the target channel for update.
@@ -23,7 +25,7 @@
 	AttemptUpdate() error
 
 	// GetStatus returns the update status.
-	GetStatus() (response UEGetStatusResponse, err error)
+	GetStatus() (*policymanagerproto.StatusResult, error)
 
 	// SubscribeStatusUpdate subscribes the status update of update-engine.
 	// It returns a channel so that any udpate status change of udpate-engine
diff --git a/src/policy_manager/dbus/update_engine_client_impl.go b/src/policy_manager/dbus/update_engine_client_impl.go
index 420a3aa..35e6742 100644
--- a/src/policy_manager/dbus/update_engine_client_impl.go
+++ b/src/policy_manager/dbus/update_engine_client_impl.go
@@ -16,21 +16,23 @@
 
 import (
 	"fmt"
+	"policy_manager/policymanagerproto"
 	"strings"
 
 	godbus "github.com/godbus/dbus"
+	"github.com/golang/protobuf/proto"
 )
 
 // Implementation of the UpdateEngineClient interface that interacts with update-engine.
 
 const (
-	updateEngineObjectName = "org.chromium.UpdateEngine"
-	updateEngineObjectPath = "/org/chromium/UpdateEngine"
-	setChannelMethod       = "org.chromium.UpdateEngineInterface.SetChannel"
-	attemptUpdateMethod    = "org.chromium.UpdateEngineInterface.AttemptUpdate"
-	getStatusMethod        = "org.chromium.UpdateEngineInterface.GetStatus"
-	statusUpdateSignal     = "org.chromium.UpdateEngineInterface.StatusUpdate"
-	dBusAddMatchMethod     = "org.freedesktop.DBus.AddMatch"
+	updateEngineObjectName  = "org.chromium.UpdateEngine"
+	updateEngineObjectPath  = "/org/chromium/UpdateEngine"
+	setChannelMethod        = "org.chromium.UpdateEngineInterface.SetChannel"
+	attemptUpdateMethod     = "org.chromium.UpdateEngineInterface.AttemptUpdate"
+	getStatusAdvancedMethod = "org.chromium.UpdateEngineInterface.GetStatusAdvanced"
+	statusUpdateSignal      = "org.chromium.UpdateEngineInterface.StatusUpdate"
+	dBusAddMatchMethod      = "org.freedesktop.DBus.AddMatch"
 )
 
 type updateEngineClientImpl struct {
@@ -72,15 +74,18 @@
 
 // GetStatus returns the update status
 func (client *updateEngineClientImpl) GetStatus() (
-	response UEGetStatusResponse, err error) {
-	err = client.conn.Object(updateEngineObjectName, updateEngineObjectPath).Call(
-		getStatusMethod, 0).Store(
-		&response.LastCheckedTime,
-		&response.Progress,
-		&response.UpdateStatus,
-		&response.NewVersion,
-		&response.NewSize)
-	return
+	*policymanagerproto.StatusResult, error) {
+	var updateStatusBytes []byte
+	updateStatus := new(policymanagerproto.StatusResult)
+	if err := client.conn.Object(updateEngineObjectName, updateEngineObjectPath).Call(
+		getStatusAdvancedMethod, 0).Store(&updateStatusBytes); err != nil {
+		return nil, fmt.Errorf("failed to fetch update status: %s", err)
+	}
+	if err := proto.Unmarshal(updateStatusBytes, updateStatus); err != nil {
+		return nil, fmt.Errorf("failed to unmarshal UpdateStatus %s: %s",
+			updateStatusBytes, updateStatus)
+	}
+	return updateStatus, nil
 }
 
 // SubscribeStatusUpdate subscribes the status update of update-engine.
diff --git a/src/policy_manager/dbus/update_engine_client_impl_test.go b/src/policy_manager/dbus/update_engine_client_impl_test.go
index 074fe46..0e8b3c5 100644
--- a/src/policy_manager/dbus/update_engine_client_impl_test.go
+++ b/src/policy_manager/dbus/update_engine_client_impl_test.go
@@ -17,6 +17,8 @@
 
 import (
 	"errors"
+	"github.com/golang/protobuf/proto"
+	"policy_manager/policymanagerproto"
 	"reflect"
 	"testing"
 
@@ -122,25 +124,28 @@
 
 // TestGetStatus tests GetStatus returns correct values.
 func TestGetStatus(t *testing.T) {
+	rebootStatus := policymanagerproto.Operation_UPDATED_NEED_REBOOT
 	tests := []struct {
 		name           string
-		expectedReturn UEGetStatusResponse
+		expectedReturn *policymanagerproto.StatusResult
 		expectError    bool
 	}{
 		{
 			"NormalCase",
-			UEGetStatusResponse{
-				1433971203,
-				0.000000,
-				"UPDATE_STATUS_UPDATED_NEED_REBOOT",
-				"7150.0.0",
-				66542575,
+			&policymanagerproto.StatusResult{
+				NewVersion:      proto.String("16550.0.0"),
+				UpdateStatus:    &rebootStatus,
+				LastCheckedTime: proto.Int64(1433971203),
 			},
 			false,
 		},
 		{
 			"ErrorCase",
-			UEGetStatusResponse{},
+			&policymanagerproto.StatusResult{
+				NewVersion:      proto.String("16550.0.0"),
+				UpdateStatus:    nil,
+				LastCheckedTime: proto.Int64(1433971203),
+			},
 			true,
 		},
 	}
@@ -149,21 +154,21 @@
 		// Mock dbus connection and object.
 		fakeConn := NewFakeConn()
 		fakeObj := NewFakeBusObject()
+		updateStatusBytes, err := proto.Marshal(test.expectedReturn)
+		if err != nil {
+			t.Errorf("Failed to marshal UpdateStatusBytes %v", updateStatusBytes)
+		}
 		fakeCall := func(args ...interface{}) ([]interface{}, error) {
 			if !test.expectError {
 				return []interface{}{
-					test.expectedReturn.LastCheckedTime,
-					test.expectedReturn.Progress,
-					test.expectedReturn.UpdateStatus,
-					test.expectedReturn.NewVersion,
-					test.expectedReturn.NewSize,
+					updateStatusBytes,
 				}, nil
 			} else {
 				return nil, errors.New("Expected error.")
 			}
 		}
 		fakeConn.SetObject(updateEngineObjectName, updateEngineObjectPath, fakeObj)
-		fakeObj.SetCall(getStatusMethod, fakeCall)
+		fakeObj.SetCall(getStatusAdvancedMethod, fakeCall)
 
 		ueClient := newFakeUpdateEngineClient(fakeConn)
 		ret, err := ueClient.GetStatus()
@@ -171,7 +176,7 @@
 		if err == nil {
 			if test.expectError {
 				t.Errorf("Test %s didn't get expected error", test.name)
-			} else if !reflect.DeepEqual(test.expectedReturn, ret) {
+			} else if !proto.Equal(test.expectedReturn, ret) {
 				t.Errorf("Test %s got unexpected return %v, want %v", test.name, ret, test.expectedReturn)
 			}
 		} else if !test.expectError {
diff --git a/src/policy_manager/imgstatus/reporter_impl.go b/src/policy_manager/imgstatus/reporter_impl.go
index db4104d..5bf09cf 100644
--- a/src/policy_manager/imgstatus/reporter_impl.go
+++ b/src/policy_manager/imgstatus/reporter_impl.go
@@ -134,7 +134,7 @@
 	// Parse lsb-release file as key value pairs.
 	pairs, err := parseKeyValuePairs(bytes.NewReader(content))
 	if err != nil {
-		return nil, fmt.Errorf("Unable to parse '%s': %s", lsbFile, err)
+		return nil, fmt.Errorf("unable to parse '%s': %s", lsbFile, err)
 	}
 
 	osVersion := new(pmpb.OSVersion)
@@ -171,19 +171,18 @@
 
 	status := new(pmpb.InstanceStatus)
 
-	// Set update status string.
-	statVal, ok := pmpb.InstanceStatus_UpdateStatus_value[response.UpdateStatus]
-	if ok {
-		statEnumVal := pmpb.InstanceStatus_UpdateStatus(statVal)
-		status.UpdateStatus = &statEnumVal
+	// Set update status.
+	status.UpdateStatus = response.UpdateStatus
+	if status.UpdateStatus == nil {
+		return nil, fmt.Errorf("failed to fetch update status: It is possible that update_engine has not fetched the status yet.")
 	}
 
 	// Set new OS version.
 	status.NewOsVersion = new(pmpb.OSVersion)
-	status.NewOsVersion.VersionString = proto.String(response.NewVersion)
+	status.NewOsVersion.VersionString = proto.String(*response.NewVersion)
 
 	// Set last update check timestamp.
-	status.UpdateCheckTimestamp = proto.Int64(response.LastCheckedTime)
+	status.UpdateCheckTimestamp = proto.Int64(*response.LastCheckedTime)
 
 	return status, nil
 }
@@ -224,7 +223,7 @@
 	// Get update status from update-engine via dbus.
 	updateStatus, err := getStatusFromUpdateEngine(rep.ueClient)
 	if err != nil {
-		status.UpdateStatus = pmpb.InstanceStatus_UPDATE_STATUS_UNKNOWN.Enum()
+		status.UpdateStatus = nil
 		status.Error = proto.String(err.Error())
 		return status, err
 	}
diff --git a/src/policy_manager/imgstatus/reporter_impl_test.go b/src/policy_manager/imgstatus/reporter_impl_test.go
index ea2f5c6..72839ce 100644
--- a/src/policy_manager/imgstatus/reporter_impl_test.go
+++ b/src/policy_manager/imgstatus/reporter_impl_test.go
@@ -252,21 +252,19 @@
 // TestGetStatusFromUpdateEngine tests that we can correctly get the update
 // status from update-engine via dbus.
 func TestGetStatusFromUpdateEngine(t *testing.T) {
-	rebootStatus := policymanagerproto.InstanceStatus_UPDATE_STATUS_UPDATED_NEED_REBOOT
+	rebootStatus := policymanagerproto.Operation_UPDATED_NEED_REBOOT
 	tests := []struct {
 		name              string
-		getStatusResponse dbus.UEGetStatusResponse
+		getStatusResponse *policymanagerproto.StatusResult
 		expectedRet       *policymanagerproto.InstanceStatus
 		expectedError     error
 	}{
 		{
 			"GoodUpdateEngineOutput",
-			dbus.UEGetStatusResponse{
-				int64(1433971203),
-				0.000000,
-				"UPDATE_STATUS_UPDATED_NEED_REBOOT",
-				"16550.0.0",
-				int64(66542575),
+			&policymanagerproto.StatusResult{
+				NewVersion:      proto.String("16550.0.0"),
+				UpdateStatus:    &rebootStatus,
+				LastCheckedTime: proto.Int64(1433971203),
 			},
 			&policymanagerproto.InstanceStatus{
 				NewOsVersion: &policymanagerproto.OSVersion{
@@ -279,12 +277,10 @@
 		},
 		{
 			"BadUpdateEngineOutput",
-			dbus.UEGetStatusResponse{
-				0,
-				0,
-				"",
-				"",
-				0,
+			&policymanagerproto.StatusResult{
+				NewVersion:      proto.String(""),
+				UpdateStatus:    nil,
+				LastCheckedTime: proto.Int64(0),
 			},
 			nil,
 			errors.New("GetStatus error"),
@@ -330,13 +326,13 @@
 	mockAPI.EXPECT().ReadFile("/etc/lsb-release").Return([]byte(testLSB), nil)
 
 	mockClient := mockdbus.NewMockUpdateEngineClient(mockCtrl)
+	rebootStatus := policymanagerproto.Operation_UPDATED_NEED_REBOOT
+
 	mockClient.EXPECT().GetStatus().Return(
-		dbus.UEGetStatusResponse{
-			int64(1433971203),
-			0.000000,
-			"UPDATE_STATUS_UPDATED_NEED_REBOOT",
-			"16550.0.0",
-			int64(66542575),
+		&policymanagerproto.StatusResult{
+			NewVersion:      proto.String("16550.0.0"),
+			UpdateStatus:    &rebootStatus,
+			LastCheckedTime: proto.Int64(1433971203),
 		}, nil)
 
 	mockPolicyEnforcer := mockpolicyenforcer.NewMockPolicyEnforcer(mockCtrl)
@@ -359,13 +355,11 @@
 		VersionString: proto.String("16550.0.0"),
 	}
 
-	updateStatus := policymanagerproto.InstanceStatus_UPDATE_STATUS_UPDATED_NEED_REBOOT
-
 	want := &policymanagerproto.InstanceStatus{
 		InstanceId:           proto.Uint64(testInstanceID),
 		OsVersion:            &currentVersion,
 		NewOsVersion:         &newVersion,
-		UpdateStatus:         &updateStatus,
+		UpdateStatus:         &rebootStatus,
 		UpdateCheckTimestamp: proto.Int64(1433971203),
 		HealthMonitorStatus: &policymanagerproto.HealthMonitorStatus{
 			Logging:    proto.Bool(true),
@@ -422,11 +416,16 @@
 			Return([]byte{}, test.lsbErr).AnyTimes()
 
 		mockClient := mockdbus.NewMockUpdateEngineClient(mockCtrl)
+		errorStatus := policymanagerproto.Operation_ERROR
+
 		// Will only call GetStatus() if we didn't get error from ReadFile().
 		if test.lsbErr == nil {
 			mockClient.EXPECT().GetStatus().Return(
-				dbus.UEGetStatusResponse{
-					int64(0), float64(0), "", "", int64(0)},
+				&policymanagerproto.StatusResult{
+					NewVersion:      proto.String(""),
+					UpdateStatus:    &errorStatus,
+					LastCheckedTime: proto.Int64(0),
+				},
 				test.updateEngineErr)
 		}
 
diff --git a/src/policy_manager/main.go b/src/policy_manager/main.go
index 85de4dd..362ea15 100644
--- a/src/policy_manager/main.go
+++ b/src/policy_manager/main.go
@@ -278,18 +278,18 @@
 	resolveEnforcementConfig(userConfig, manager)
 
 	if err = resolveLocalUpdateStrategy(userConfig, status.GetOsVersion()); err != nil {
-		return fmt.Errorf("Error resolving update strategy (%s). Instance config will not be applied.", err)
+		return fmt.Errorf("error resolving update strategy (%s). Instance config will not be applied.", err)
 	}
 
 	if isUpdateDisabled(userConfig) {
 		glog.Info("Detected updates disabled: stopping update engine...")
 		if err := stopUpdateEngine(false); err != nil {
-			glog.Errorf("Failed to stop update engine: %v", err)
+			glog.Errorf("failed to stop update engine: %v", err)
 		}
 	}
 
 	if err = switchChannelIfNeeded(ueClient, status, userConfig); err != nil {
-		return fmt.Errorf("Error switching channel (%s). Instance config will not be applied.", err)
+		return fmt.Errorf("error switching channel (%s). Instance config will not be applied.", err)
 	}
 
 	return manager.SetInstanceConfig(userConfig)
@@ -313,11 +313,11 @@
 			channelConfig := fmt.Sprintf("%s-channel", targetChannel)
 
 			if err := ueClient.SetChannel(channelConfig); err != nil {
-				glog.Errorf("Channel switch from %s to %s failed: %s.",
+				glog.Errorf("channel switch from %s to %s failed: %s.",
 					currentChannel, targetChannel, err)
 				return err
 			} else {
-				glog.Infof("Channel switch from %s to %s succeeded.",
+				glog.Infof("channel switch from %s to %s succeeded.",
 					currentChannel, targetChannel)
 			}
 		}
@@ -332,7 +332,7 @@
 	match := imageNamePattern.FindStringSubmatch(strategy)
 
 	if match == nil {
-		return nil, fmt.Errorf("Error parsing update_strategy: %s", strategy)
+		return nil, fmt.Errorf("error parsing update_strategy: %s", strategy)
 	}
 
 	channelValue := policymanagerproto.ReleaseChannel_value[strings.ToUpper(match[2])]
@@ -355,7 +355,7 @@
 	if ueStatus.UpdateStatus == "UPDATE_STATUS_UPDATED_NEED_REBOOT" {
 		config, err := manager.GetInstanceConfig()
 		if err != nil {
-			return fmt.Errorf("Failed to get Instance config: %v, "+
+			return fmt.Errorf("failed to get Instance config: %v, "+
 				"ignore UPDATED_NEED_REBOOT", err)
 		}
 
@@ -368,11 +368,11 @@
 			glog.Infof("New COS version %v has been installed. Rebooting...", ueStatus.NewVersion)
 			_, _, err := apiHandler.RunCommand("/sbin/shutdown", "-r", "now")
 			if err != nil {
-				return fmt.Errorf("Reboot attempt failed: %v", err)
+				return fmt.Errorf("reboot attempt failed: %v", err)
 			}
 		} else {
 			return fmt.Errorf(
-				"Installed new version %v doesn't match "+
+				"installed new version %v doesn't match "+
 					"instance config target_version_prefix %v, "+
 					"ignore UPDATED_NEED_REBOOT",
 				ueStatus.NewVersion, config.GetTargetVersionPrefix())
@@ -395,7 +395,7 @@
 		glog.Info("Detected updates disabled: stopping update engine...")
 		// Use --no-block because update-engine depends on init-device-policy
 		if err := stopUpdateEngine(true); err != nil {
-			glog.Errorf("Failed to stop update-engine: %v", err)
+			glog.Errorf("failed to stop update-engine: %v", err)
 		}
 	}
 
@@ -606,7 +606,7 @@
 	case showConfigCommand:
 		handleShowConfigCmd(args)
 	default:
-		glog.Errorf("Unrecognized command: %s\n", command)
+		glog.Errorf("unrecognized command: %s\n", command)
 		flag.Usage()
 	}
 
diff --git a/src/policy_manager/policymanagerproto/instance_status.proto b/src/policy_manager/policymanagerproto/instance_status.proto
index ac0f511..1c30681 100644
--- a/src/policy_manager/policymanagerproto/instance_status.proto
+++ b/src/policy_manager/policymanagerproto/instance_status.proto
@@ -15,6 +15,8 @@
 // Protobuf definitions for reporting instance status.
 syntax = "proto2";
 
+import "policy_manager/policymanagerproto/update_status.proto";
+
 package policymanagerproto;
 
 // ReleaseChannel specifies the Chrome OS release channel that the image is on.
@@ -124,7 +126,7 @@
   }
 
   // The current update status as reported by the update_engine.
-  optional UpdateStatus update_status = 3 [default = UPDATE_STATUS_UNKNOWN];
+  optional Operation update_status = 3 [default = ERROR];
 
   // New version delivered by the update.
   // This fields is only useful if the update engine status is one of the
diff --git a/src/policy_manager/policymanagerproto/update_status.proto b/src/policy_manager/policymanagerproto/update_status.proto
new file mode 100644
index 0000000..3e06a8f
--- /dev/null
+++ b/src/policy_manager/policymanagerproto/update_status.proto
@@ -0,0 +1,98 @@
+// Copyright 2022 Google LLC
+//
+// 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.
+
+// Protobuf definitions for Update Status of COS instance. Keep this file
+// synced with
+// chromiumos/src/platform2/system_api/dbus/update_engine/update_engine.proto.
+syntax = "proto2";
+
+package policymanagerproto;
+
+// Keep in sync with:
+// update_engine/client_library/include/update_engine/update_status.h
+enum Operation {
+  IDLE = 0;
+  CHECKING_FOR_UPDATE = 1;
+  UPDATE_AVAILABLE = 2;
+  DOWNLOADING = 3;
+  VERIFYING = 4;
+  FINALIZING = 5;
+  UPDATED_NEED_REBOOT = 6;
+  REPORTING_ERROR_EVENT = 7;
+  ATTEMPTING_ROLLBACK = 8;
+  DISABLED = 9;
+  NEED_PERMISSION_TO_UPDATE = 10;
+
+  // ERROR is only used by Chrome and update_engine doesn't really use/set this
+  // value as it is not the proper way to propagate errors. DO NOT use this
+  // anywhere other than Chrome.
+  // TODO(crbug.com/977320): Remove this from Chrome.
+  ERROR = -1;
+}
+
+// Keep in sync with:
+// update_engine/client_library/include/update_engine/update_status.h
+enum UpdateUrgency {
+  REGULAR = 0;
+  CRITICAL = 1;
+}
+
+// This is the message transferred between update_engine and other processes
+// about the current status of the update_engine. It is used either in
+// |GetUpdateStatusAdvanced| DBus method or |StatusUpdateAdvanced| signal.
+//
+// NOTE: Keep this updated with:
+// update_engine/client_library/include/update_engine/update_status.h
+message StatusResult {
+  // When the update_engine last checked for updates (time_t: seconds from Unix
+  // epoch).
+  optional int64 last_checked_time = 1;
+
+  // The current progress (0.0f-1.0f).
+  optional double progress = 2;
+
+  // The current status/operation of the update_engine.
+  optional Operation update_status = 3;
+
+  // The new product version.
+  optional string new_version = 4;
+
+  // The size of the update payload (bytes).
+  optional int64 new_size = 5;
+
+  // Whether the update is actually an enterprise rollback. The value is valid
+  // only if the current_operation is passed |CHECKING_FOR_UPDATE|.
+  optional bool is_enterprise_rollback = 6;
+
+  // Indication of install for DLC(s).
+  optional bool is_install = 7;
+
+  // The end-of-life date of the device in the number of days since Unix Epoch.
+  optional int64 eol_date = 8;
+
+  // If true, the system will powerwash once the update is applied and the
+  // system is rebooted. This value is reliable on |UPDATE_NEED_REBOOT|.
+  optional bool will_powerwash_after_reboot = 9;
+
+  // The last update/install attempt error.
+  // Reference update_engine/dbus-constants.h for the errors that update_engine
+  // will propagate into this field.
+  optional int32 last_attempt_error = 10;
+
+  // If true, the update is critical. DEPRECATED.
+  // bool critical_update = 11;
+
+  // Indicates how important an update is.
+  optional UpdateUrgency update_urgency = 12;
+}
diff --git a/src/policy_manager/policymanagerutil/config_fetcher.go b/src/policy_manager/policymanagerutil/config_fetcher.go
index 2c4d03d..33422a7 100644
--- a/src/policy_manager/policymanagerutil/config_fetcher.go
+++ b/src/policy_manager/policymanagerutil/config_fetcher.go
@@ -114,16 +114,16 @@
 	var err error
 
 	if rawMetadata, etag, err = retriever.FetchMetadata(lastEtag); err != nil {
-		glog.Errorf("Failed to fetch metadata from metadata server: %s", err)
+		glog.Errorf("failed to fetch metadata from metadata server: %s", err)
 		return nil, "", err
 	}
 
 	if metadata, err = parseRawMetadata(rawMetadata); err != nil {
-		glog.Errorf("Failed to parse raw metadata: %s", err)
+		glog.Errorf("failed to parse raw metadata: %s", err)
 		return nil, "", err
 	}
 
-	glog.V(4).Infof("Current value of instance custom metadata directory: %v", metadata)
+	glog.V(4).Infof("current value of instance custom metadata directory: %v", metadata)
 	userConfig := getUserConfigFromMetadata(metadata)
 	return userConfig, etag, nil
 }
@@ -165,7 +165,7 @@
 	if configStr, ok = metadata[gciLegacyConfigKey]; ok {
 		if configStr != "" {
 			if err = jsonpb.UnmarshalString(configStr, userConfig); err != nil {
-				glog.Errorf("Failed to unmarshal InstanceConfig %s: %s", configStr, err)
+				glog.Errorf("failed to unmarshal InstanceConfig %s: %s", configStr, err)
 			}
 		}
 		return userConfig