-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
instances: Patch CNS processing during instance creation #32
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One minor nit, but other than that this looks correct to me. 👍 for the test.
compute/instances.go
Outdated
@@ -963,8 +974,7 @@ func (api *_Instance) toNative() (*Instance, error) { | |||
// submit an API call to the vmapi machine endpoint | |||
func (mcns *InstanceCNS) toTags(m map[string]interface{}) { | |||
if mcns.Disable != nil { | |||
s := fmt.Sprintf("%t", mcns.Disable) | |||
m[CNSTagDisable] = &s | |||
m[CNSTagDisable] = fmt.Sprintf("%t", *mcns.Disable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should likely use strconv.FormatBool
from the standard library instead of fmt.Sprintf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, I think I'm going to remove the pointers here as well. I'm not sure why they're used by the InstanceCNS
struct values.
Actually, looks like there are some changes for supporting CNS updates through the ReplaceTags endpoint. I'll include them here as well. |
Disable *bool | ||
ReversePTR *string | ||
Disable bool | ||
ReversePTR string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be fine changing these semantics considering this struct was renamed under the new API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
Nevermind... found another last minute change. |
compute/instances.go
Outdated
m[CNSTagDisable] = &s | ||
func (cns *InstanceCNS) toTags(m map[string]interface{}) { | ||
if cns.Disable { | ||
m[CNSTagDisable] = cns.Disable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just confirmed that this actually needs to be a boolean and posted as such through the tags API (as JSON).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bool
or a "string masquerading as a bool
" ? Regardless, this may be worth calling out as a comment in the code:
// NOTE(justinwr@): The API requires the CNSTagDisable attribute to be a boolean, not a bool string.
That comment probably needs to chase into a Validator()
function, actually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bool
bool
since the JSON encoder will keep it a boolean. Doesn't look like we had any other examples before this.
I'll add the comment here as well.
Ok, this is ready for review again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few misc items and comments.
compute/instances.go
Outdated
input.CNS.toTags(result) | ||
for key, value := range result { | ||
if key == CNSTagServices { | ||
result[fmt.Sprintf("tag.%s", key)] = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually one of those cases where I'd skip fmt.Sprintf()
and would just use +
to concatenate the two strings.
That said, can you sanitize the input for key
to make sure that it conforms to what we're expecting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can definitely clean this up a bit and rely more on CNSTagServices
. That is the only tag we need to prepend with tag.
here, anything else slips through.
compute/instances.go
Outdated
} | ||
|
||
// toAPI() merges both Tags and CNS tags into the same map of strings used as | ||
// the body of our ReplaceTags request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Append something like:
Used to join Tags and CNS tags into the same JSON object before sending an API request to the API gateway.
So that in the future we know we should be able to remove this method if/when CNS and Tags become first-class API citizens with their own API endpoints.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
compute/instances.go
Outdated
func (input ReplaceTagsInput) toAPI() map[string]interface{} { | ||
result := map[string]interface{}{} | ||
for key, value := range input.Tags { | ||
result[key] = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values have been sanitized at this point, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're alright since we're liberal with what can be a tag and we're also relying on the JSON encoder to marshal types going into CloudAPI. But I can see how the use of interface{}
would normally allow other types through in cases like this.
compute/instances.go
Outdated
m[CNSTagDisable] = &s | ||
func (cns *InstanceCNS) toTags(m map[string]interface{}) { | ||
if cns.Disable { | ||
m[CNSTagDisable] = cns.Disable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bool
or a "string masquerading as a bool
" ? Regardless, this may be worth calling out as a comment in the code:
// NOTE(justinwr@): The API requires the CNSTagDisable attribute to be a boolean, not a bool string.
That comment probably needs to chase into a Validator()
function, actually.
if len(mcns.Services) > 0 { | ||
m[CNSTagServices] = strings.Join(mcns.Services, ",") | ||
if len(cns.Services) > 0 { | ||
m[CNSTagServices] = strings.Join(cns.Services, ",") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to get a stable comparison, in both the *Read call and here, do we need to sort cns.Services
? I don't know but seem to recall there wasn't a sorting guarantee here (I also don't think we should depend on it if there is one currently).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say this is something Terraform should handle rather than the library since that is where the sorted version is required.
@@ -30,6 +35,202 @@ func getAnyInstanceID(t *testing.T, client *compute.ComputeClient) (string, erro | |||
return "", errors.New("no machines configured") | |||
} | |||
|
|||
func RandInt() int { | |||
reseed() | |||
return rand.New(rand.NewSource(time.Now().UnixNano())).Int() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to stump the seeding library here, either for explicit use or to crib an example from: https://github.com/sean-/seed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given we actually need cryptographically secure random number generation in the authentication
package (ref https://github.com/joyent/triton-go/blob/5f7872646922b73632f434afa5e45fcd18aa9d5b/authentication/private_key_signer.go#L77) which is imported into the client, which is in turn imported everywhere, maybe we should put any seeding there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I agree and I'll fit this into a secondary PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref: #33 ✔️
examples/compute/create_instance.go
Outdated
|
||
func main() { | ||
keyID := os.Getenv("SDC_KEY_ID") | ||
accountName := os.Getenv("SDC_ACCOUNT") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be worth using os.LookupEnv
here instead. This is just an example, but these values can't be empty strings without blowing up the downstream callers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
examples/compute/create_instance.go
Outdated
createInput := &compute.CreateInstanceInput{ | ||
Name: "go-test1", | ||
Package: "g4-highcpu-512M", | ||
Image: "1f32508c-e6e9-11e6-bc05-8fea9e979940", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What datacenter is this instance out of? It may be worth noting the image ID is tied to a DC atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was sloppy. I had written out the example for someone else and refactored into the test. I just need to pull the image ID dynamically as I did there.
examples/compute/create_instance.go
Outdated
|
||
select { | ||
case instance := <-state: | ||
fmt.Println("\nDuration:", time.Since(startTime).String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fmt.Printf("\nDuration: %s\n", time.Since(startTime))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
log.Fatalf("Create(): %v", err) | ||
} | ||
|
||
// Wait for provisioning to complete... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be a helper function that we write and expose to consumers because most API consumers are going to want a synchronous API and semantics (including Terraform), not async.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree. I'll push a PR separately for that so we can define the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable. We likely want to define a uniform way of dealing with this for as many resources as possible in order to reduce the amount of API internals which need to be understood to consume this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref: #34 ✔️
Think we're good to merge here. |
Looks good to me! |
CNS was not respected when creating a new instance. This addresses that concern, adds a thorough acceptance test for instance creation, and includes an example for others.