-
Notifications
You must be signed in to change notification settings - Fork 853
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
Alpha of ARM packages #185
Conversation
Signed-off-by: Brendan Dixon <brendand@microsoft.com>
Hi @brendandixon, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution! TTYL, MSBOT; |
@ahmetalpbalkan @paulmey @devigned Ok folks. Here's an alpha of the ARM packages. |
@brendandixon, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR. |
|
||
func NewWithBaseUri(baseUri string, subscriptionId string) *StorageManagementClient { | ||
client := &StorageManagementClient{BaseUri: baseUri, SubscriptionId: subscriptionId} | ||
client.PollingMode = autorest.PollUntilDuration |
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.
area for little idiomatic go improvement: these can probably go into the struct initializer above.
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.
Not sure I follow. These routines are called by other routines, some of which may provide new values.
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 mean you can replace:
client := &StorageManagementClient{BaseUri: baseUri, SubscriptionId: subscriptionId}
client.PollingMode = autorest.PollUntilDuration
with the following:
return &StorageManagementClient{
BaseUri: baseUri,
SubscriptionId: subscriptionId,
Client: autorest.Client{
PollingMode: autorest.PollUntilDuration,
}}
so you don't mix “assignments” with struct “initializers” and you can just return
this single statement..
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.
@ahmetalpbalkan Got it. The pronoun above w/o a referent left the question in my mind.
Here's my first main comment: I see this pattern in pretty much a lot of packages.
in pretty much all packages. I think this is
If I'm calling into 5 different packages, I'm initializing 5 different clients by passing my subscription ID 5 times. This doesn't make sense to me: sc := storage.New(MY_SUBS_ID)
// sc.doThis();
net := network.New(MY_SUBS_ID)
// net.doThat();
co := compute.New(MY_SUBS_ID)
// co.doThat(); I think a better model (and what we follow in the azClient := azure.New(MY_SUBS_ID, AND_BUNCH_OF_OTHER_THINGS
LIKE_RETRY_POLICY,USER-AGENT,POLLING_INTERVAL_WHATEVER)
net := network.New(azClient)
stg := storage.New(azClient)
cmp := compute.New(azClient) Here's what I just achieved with this:
It would be great if we can fix it this way. @brendandixon if you also agree, I'd like to take this fix because it will be breaking change. |
[followup to the comment above] We have this model already in
You can see how tiny the individual |
/cc @Azure/adx-sdk-team |
@ahmetalpbalkan We should chat. First, the generator must handle more than just ARM clients. It does not know that these packages are related and, hence, cannot divine the need for a common "base" class. Second, the go-autorest package provides a "base" class used by all generated classes (autorest.Client) which provides public access to everything and more (e.g. client.PollingDuration, client.PollingMode). It also provides easy-to-use "hooks" by which to provide a modified version of http.Client and more. So, the user has full access to everything. The rest is somewhat imposed on the code by the generator model. |
@ahmetalpbalkan By the way, in my experience, it is generally a poor practice to expose, as individual arguments, things a user may want to configure. It tends to be much too fragile. Better models, and that which I've tried to provide through the go-autorest package, allows users to make modifications w/o the core code having to know all things. |
@brendandixon fair enough. If the code generation model enforces this model, we can live with it. I guess it would be nice to have all the client configurations centralized in one place because pretty much nobody just initializes only one client, it's nearly always a mix of those. I didn't realize all those fields were public, so it's all good as long as it is configurable. |
@ahmetalpbalkan It actually supports configuration well-beyond those few values. Users may provide instances of http.Client that do whatever magic they desire (e.g., handle proxies, implement circuit breakers, log requests / responses). Further the model supports fan-in / out through channels w/o the developer having to writes lots of code. I could add an initializer / constructor that accepts, along with other values, an instantiated autorest.Client. Further reduction of repetition, given how the generator must work, is best handled via convention (e.g., using a local, well-defined Azure properties file -- which we really need to have and do not (yet) have). Such an approach will never break signatures and reduces the risk of checking in bad things (e.g., keys) to central repositories. @ahmetalpbalkan I think I've come up with a means by which the user can, if they desire, create one autorest.Client to share. I'll need to think through the cases, but I believe it will work. If it does, then I can eliminate some of the seemingly redundant code. |
return client | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// |
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.
Currently this block looks like a godoc (because it's just above the type declaration). Maybe have a new line after this comment block?
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.
@ahmetalpbalkan I'm sorry, but which line? The type declaration occurs before the two methods. Are the comment-less methods a problem? I tended to direct the comments to the block before the type.
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.
@ahmetalpbalkan Got it. Thank you. I failed to see that. The files are long and need some kind of divider. I failed to see that these collide with good. I'll fix.
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.
@brendandixon if they're indeed different clients and you're having trouble skimming long files why not split different client types into different source files such as:
- compute/availabilitysets.go
- compute/availabilitysets_entities.go
- compute/virtualmachineimages.go
- compute/virtualmachineimages_entities.go
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.
@ahmetalpbalkan Definitely an option and easy to do.
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.
@brendandixon that would be really nice to have IMO.
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.
@ahmetalpbalkan Will do. I will keep all types in a single, models.go, file, however.
} | ||
|
||
// ListPublishers gets a list of virtual machine image publishers.; | ||
// |
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.
no biggie: Looks like when the method has no parameters to document, the godoc is followed by an extra empty comment like:
// MethodName description lorem ipsum.
//
func (....
return result, fmt.Errorf("compute: Failure sending VirtualMachineExtensionImagesClient.ListVersions request (%v)", err) | ||
} | ||
|
||
result = []VirtualMachineImageResource{} |
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 there's a bit of difference between
result = []VirtualMachineImageResource{}
and
var result []VirtualMachineImageResource
. The first one actually initializes something that takes up space in memory (i.e. zero-length slice) whereas the second one is just nil
-slice. It will not cause any problems here but just to be idiomatic, I'd do var foo []T
.
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.
@ahmetalpbalkan The code is correct as it is. json.Unmarshal requires a pointer to an existing struct.
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.
@brendandixon I'm not sure what you're talking about. Both expressions I listed above are of the same types, and both will work with json.Unmarshal
as is. Check this example out: http://play.golang.org/p/P0yLMrZYfN
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.
@ahmetalpbalkan And they're the same size. See http://play.golang.org/p/Rho7YDbGDE.
Leaving it as it is leaves the generator less complicated (otherwise we have to add a case to handle 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.
@ahmetalpbalkan I was actually surprised that json.Unmarshal accepted []Foo form. Now that I see both are the same size, it is apparent that []Foo is equivalent to []Foo{}.
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.
@brendandixon umm, equivalent, not really. :) If you actually compare the two forms using ==
you'll get a false
. One does an allocation in stack, the other one doesn't.
Totally off topic but here's a benchmark: http://play.golang.org/p/XBLXdWv9_8 the one that doesn't initialize the empty slice is ~20% faster. Again, doesn't really matter in this case. It's more of a styling argument here.
✅
@brendandixon generally LGTM, feel free to merge at your own discretion. |
Alpha of generated ARM packages
Signed-off-by: Brendan Dixon brendand@microsoft.com