Automatic registration of tentacle not writing to local configuration

Hi all,

I am attempting to programmatically register polling tentacles with the server using the Octopus.Client.Model or by going directly at the Web API. The two methods I have used are below:

Via the Web API:

let octopusHeaders = [ "X-Octopus-ApiKey", octopusAPIKey ]
let mutable tenantEditor = repository.Tenants.CreateOrModify(tentacleName)
let alphaNums = Seq.toList "abcdefghijklmnopqrstuvwxyz0123456789"
let random = new Random()
let tentacleURL = sprintf "poll://%s/" (new String( ( fun _ -> alphaNums.[random.Next(36)] ) [| 0..20 |]))
let tentacleRoles = [ "web-server"; "transient" ] // Transient to allow asynchronous updates
let registrationTemplate = """ 
	"Endpoint": { "CommunicationStyle": "TentacleActive", 
				  "Thumbprint": "%s", 
				  "Address": "%s:10943",
				  "Squid": "",
				  "SubscriptionId": "%s",
				  "Uri": "%s" }, 
	"EnvironmentIDs": [ "%s" ], 
	"TenantedDeploymentParticipation": "Tenanted", 
	"TenantIds": [ "%s" ], 
	"Name": "%s", 
	"Roles": %A, 
	"Status": "Unknown",
	"IsDisabled": false 

let sprintfPayload = Printf.StringFormat<string->string->string->string->string->string->string->string list->string>(registrationTemplate)
let registrationPayloadWithSemicolons = sprintf sprintfPayload tentacleThumbprint octopusURL tentacleURL tentacleURL environment.Id tenantEditor.Instance.Id tentacleName tentacleRoles
let registrationPayload = registrationPayloadWithSemicolons.Replace(";", ",")

let registerURL = sprintf "%s/api/machines" octopusURL
let registerQuery = Register.Registration (Register.Parse registrationPayload).JsonValue 

// Synchronous call
let registerData = 
		registerQuery.JsonValue.Request(registerURL, "POST", octopusHeaders) // Synchronous call
	| :? System.Net.WebException as webEx -> 
		let matches = Regex("([0-9]{3})").Matches(webEx.Message)
		let statusCode = match matches.Count with
							| 0 -> 404
							| _ -> int (matches.Item(0).Value)
		log "ERROR" (sprintf "Registration of the machine with Octopus failed.  HTTP response code: %i" statusCode)
		logex "ERROR" webEx
		{ Body = HttpResponseBody.Text (webEx.Message)
		  StatusCode = statusCode //int webEx.Response.StatusCode
		  ResponseUrl = webEx.Response.ResponseUri.OriginalString //HelpLink
		  Headers = (Map.empty, webEx.Response.Headers.AllKeys)
					 ||> Array.fold ( fun map key -> let value = webEx.Response.Headers.[key]
													 Map.add key value map )
		  Cookies = Map.empty }

log "INFO" "Process complete."
registerData.StatusCode // Return HTTP response code.

Via the Octopus.Client.Model:

let tentacleEndpoint = new PollingTentacleEndpointResource()
tentacleEndpoint.Thumbprint <- tentacleThumbprint // The certificate thumbprint of the tentacle
tentacleEndpoint.Uri <- tentacleURL // A 20-character random alpha-numeric string becomes the mailbox to poll from
let tentacle = new MachineResource()
tentacle.Endpoint <- tentacleEndpoint
tentacle.Name <- tentacleName
tentacle.Uri <- tentacleURL
tentacle.EnvironmentIds.Add environment.Id |> ignore
tentacleRoles |> Seq.iter ( fun p -> tentacle.Roles.Add p |> ignore )
tentacle.TenantIds.Add tenantEditor.Instance.Id |> ignore
tentacle.MachinePolicyId <- (repository.MachinePolicies.FindByName "On-Premise").Id
let returnCode = try
                     repository.Machines.Create tentacle |> ignore
                     | :? System.Net.WebException as webEx -> 
                         let statusCode = int (webEx.Response :?> HttpWebResponse).StatusCode
                         log "ERROR" ( sprintf "Registration of the machine with Octopus failed: %s \r\n\r\nHTTP response code: %i" webEx.Message statusCode )

log "INFO" "Process complete."

Both of these methods successfully register the tentacle with the server. However, this is the resulting TrustedOctopusServers key in Tentacle.config:

<set key="Tentacle.Communication.TrustedOctopusServers">[{"Thumbprint":"8491ADFBAE10FD7A9CAAABEE37D6103AFE474400","CommunicationStyle":1,"Address":null,"Squid":null,"SubscriptionId":null}]</set>

Thus, the tentacle does not pass the health check. The above code samples do not modify the local Tentacle.config like the Tentacle.exe does:

"C:\Program Files\Octopus Deploy\Tentacle\Tentacle.exe" register-with --instance "Tentacle" --server "" --name "HOSTNAME" --comms-style "TentacleActive" --server-comms-port "10943" --force --apiKey "API-KEY" --environment "On-Premise" --tenanttag "Purpose/Cluster-Unaware" --tenant "TENANTNAME" --role "web-server" --role "transient" --policy "On-Premise"

Resulting in:

<set key="Tentacle.Communication.TrustedOctopusServers">[{"Thumbprint":"8491ADFBAE10FD7A9CAAABEE37D6103AFE474400","CommunicationStyle":2,"Address":"","Squid":null,"SubscriptionId":"poll://4fluhrprrh56dvhqebw6/"}]</set>

Am I missing something? Do I need to manually modify the Tentacle.config, or look for and fire off the Tentacle.exe?


A third method produced the identical result: properly registered on the Octopus server, but the local Tentacle.config remained unchanged:

let register = new Octopus.Client.Operations.RegisterMachineOperation()
register.CommunicationStyle <- CommunicationStyle.TentacleActive
register.EnvironmentNames <- [| environment.Name |]
register.MachineName <- tentacleName
register.MachinePolicy <- "On-Premise"
register.Roles <- List.toArray tentacleRoles
register.SubscriptionId <- Uri tentacleURL
register.Tenants <- [| tentacleName |]
register.TentacleThumbprint <- "657DDE8048C99BBCA4D98244F7F72DEDC24BD654"
register.Execute repository

Hi Vern

Thanks for getting in touch!

The command that you are calling here is only related to registering the tentacle on the server - it does not modify the local configuration on the machine. It doesn’t even have to be run from the same machine as the Tentacle.

This command is actually used by Tentacle.exe to register, and then based on success of that, it updates the configuration file.

Please see for some examples of how to do this with the command line.

Hope that helps!


Thanks Matt! I have scrapped the code and scripted against Tentacle.exe instead. All is well.