agent/hybrid/hybrid.go (231 lines of code) (raw):

package hybrid import ( "bytes" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "io" "os" "github.com/aliyun/aliyun_assist_client/agent/hybrid/instance" "github.com/aliyun/aliyun_assist_client/agent/log" "github.com/aliyun/aliyun_assist_client/agent/metrics" "github.com/aliyun/aliyun_assist_client/agent/util" "github.com/aliyun/aliyun_assist_client/agent/util/osutil" "github.com/aliyun/aliyun_assist_client/agent/util/serviceutil" "github.com/aliyun/aliyun_assist_client/agent/version" "github.com/aliyun/aliyun_assist_client/common/apiserver" "github.com/aliyun/aliyun_assist_client/common/httpbase" "github.com/aliyun/aliyun_assist_client/common/metaserver" "github.com/aliyun/aliyun_assist_client/thirdparty/sirupsen/logrus" "golang.org/x/net/http/httpguts" ) const ( errCodeFingerprintDuplicate = "fingerprint_duplicate" errCodeEcsRegDisabled = "ecs_registration_disabled" ) func Register(region string, code string, id string, name string, networkmode string, need_restart bool, tags []Tag) (ret bool) { logger := log.GetLogger().WithField("action", "register") logger.Infoln(region, code, id, name) if instance.IsHybrid() { fmt.Println("error, agent already register, deregister first") logger.Infoln("error, agent already register, deregister first") return } ret = false hostname, _ := os.Hostname() osType := osutil.GetOsType() defer func() { if ret { metrics.GetHybridRegisterEvent().ReportEventSync() } }() // Try to get instanceId from metaserver. instanceCh := make(chan string) go func() { // Default timeout in GetInstanceId() is 5 seconds. It's too long. instanceId, err := metaserver.GetInstanceId(logger, httpbase.WithTimeoutInSeconds(2)) if err != nil { instanceId = "" } instanceCh <- instanceId }() ip, _ := osutil.ExternalIP() var pub, pri bytes.Buffer err := genRsaKey(&pub, &pri) if err != nil { fmt.Println("error, generate rsa key failed") return } encodeString := base64.StdEncoding.EncodeToString(pub.Bytes()) mid, _ := instance.MachineID() fingerprint, err := instance.GenerateFingerprint() if err != nil { fmt.Printf("generate fingerprint failed: %v", err) return } info := &RegisterInfo{ Code: code, MachineId: mid, Fingerprint: fingerprint, RegionId: region, InstanceName: name, Hostname: hostname, IntranetIp: ip.String(), OsVersion: osutil.GetVersion(), OsType: osType, ClientVersion: version.AssistVersion, PublicKeyBase64: encodeString, Id: id, Tag: tags, } headers := make(map[string]string) // Put instanceId into headers if it exists. instanceId := <-instanceCh if len(instanceId) > 0 && len(instanceId) <= 32 && httpguts.ValidHeaderFieldValue(instanceId) { headers["X-Client-Instance-ID"] = instanceId } else { logger.Info("invalid ecs instanceId: ", instanceId) headers["X-Client-Instance-ID"] = "unknown" } resp, err := doRegister(info, networkmode, headers) if err != nil { fmt.Println("Register failed: ", err) return } if resp.Code == 200 { instance.SaveInstanceInfo(resp.InstanceId, fingerprint, region, pub.String(), pri.String(), networkmode) } else if resp.ErrCode == errCodeEcsRegDisabled { fmt.Println("The ECS instance is forbidden from registering as a managed instance.") return } else if resp.ErrCode == errCodeFingerprintDuplicate { fmt.Println("Fingerprint is duplicated and needs to be regenerated") fingerprint, err = instance.GenerateFingerprintIgnoreSavedHash() if err != nil { fmt.Println("Regenerate fingerprint failed: ", err) return } info.Fingerprint = fingerprint resp, err = doRegister(info, networkmode, headers) if err == nil && resp.Code == 200 { instance.SaveInstanceInfo(resp.InstanceId, fingerprint, region, pub.String(), pri.String(), networkmode) } else { fmt.Println("Register failed: ", err, resp) return } } else { fmt.Println("Register failed: ", resp) return } fmt.Println("register ok") fmt.Println("instance id:", resp.InstanceId) if need_restart { serviceutil.RestartAgentService(logger) } fmt.Println("restart service") ret = true return } func UnRegister(need_restart bool) bool { logger := log.GetLogger().WithFields(logrus.Fields{ "action": "deregister", "need_restart": need_restart, }) if !instance.IsHybrid() { fmt.Println("There's no need to unregister it, as it is not a hybrid instance.") return false } metrics.GetHybridUnregisterEvent().ReportEvent() url := util.GetDeRegisterService() logger.Info("deregister service url: ", url) response, err := util.HttpPost(url, "", "") if err != nil { fmt.Println(response) } ret := true var unregister_response unregisterResponse if err := json.Unmarshal([]byte(response), &unregister_response); err == nil { if unregister_response.Code == 200 { ret = true } else { ret = false } } if !ret { fmt.Println("unregister failed") fmt.Println(response) } else { fmt.Println("unregister ok") clean_unregister_data(logger, need_restart) } return ret } // In previous versions, we used the fingerprint generated by the Agent itself as // the unique ID of hybrid instance and saved it in the hybrid/fingerprint file. // The hybrid/machine-id still stored the original ID, which caused ambiguity. // Now we discard the hybrid/fingerprint file and save the fingerprint directly to // the hybrid/machine-id file. // So we need to check the hybrid/fingerprint file. If it exists, copy the // fingerprint to hybrid/machind-id and delete the hybrid/fingerprint file. func CheckFingerprint() { if !instance.IsHybrid() { return } logger := log.GetLogger().WithField("phase", "CheckFingerprint") fingerprint := instance.ReadDiscardFingerprintFile() if fingerprint != "" { logger.Infof("Move fingerprint[%s] from hybrid/fingerprint to hybrid/machine-id", fingerprint) if err := instance.SaveFingerprint(fingerprint); err != nil { logger.Errorf("Save fingerprint[%s] failed: %v", fingerprint, err) } else { instance.DeleteDiscardFingerprintFile() logger.Infof("Save fingerprint[%s] success, delete discard fingerprint file", fingerprint) } } } func doRegister(info *RegisterInfo, networkmode string, headers map[string]string) (resp registerResponse, err error) { jsonBytes, _ := json.Marshal(*info) url := util.GetRegisterService(info.RegionId, networkmode) log.GetLogger().Info("register service url: ", url) content, err := apiserver.HttpPostWithSpecifiedHeader(log.GetLogger(), url, string(jsonBytes), "", headers) if err != nil { log.GetLogger().Info("register request failed: ", err) return } err = json.Unmarshal([]byte(content), &resp) return } // RSA公钥私钥产生 func genRsaKey(pub io.Writer, pri io.Writer) error { // 生成私钥文件 privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return err } derStream := x509.MarshalPKCS1PrivateKey(privateKey) block := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: derStream, } err = pem.Encode(pri, block) if err != nil { return err } // 生成公钥文件 publicKey := &privateKey.PublicKey derPkix, err := x509.MarshalPKIXPublicKey(publicKey) if err != nil { return err } block = &pem.Block{ Type: "PUBLIC KEY", Bytes: derPkix, } err = pem.Encode(pub, block) if err != nil { return err } return nil } func clean_unregister_data(logger logrus.FieldLogger, need_restart bool) { instance.RemoveInstanceInfo() // update hardwareInfo and hope this instance will be recognized when next registration if hardwareInfo, err := instance.ReadHardwareInfo(); err == nil { if currentHardwareInfo, err := instance.CurrentHwHash(); err == nil { instance.SaveHardwareInfo(hardwareInfo.Fingerprint, currentHardwareInfo) } } if need_restart { serviceutil.RestartAgentService(logger) fmt.Println("restart service") } }