-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Begin implementation of Enterprise ManagementConsole API #1269
Begin implementation of Enterprise ManagementConsole API #1269
Conversation
1cb61c0
to
b83cf88
Compare
@shiftkey just a gentle reminder about this PR 😀 I know you've been travelling etc, I've been a bit preoccupied with some other things recently myself, but should be ready to get back into finishing off the enterprise api's now, pending advice/feedback on what I'd found with this PR |
…eciton, to achieve managemet console access rather than requiring a specific GitHubClient that cant call normal API's Instead, the management client methods can check the base Url and if it contains /api/v3/ they can set their relative endpoint Uri to include a leading "/" which will cause the /api/v3/ to be removed.
Fix xml comments
Fix xml comments
4942393
to
d9fec7e
Compare
…e-maintenance Conflicts: Octokit.Reactive/Octokit.Reactive-Mono.csproj Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj Octokit.Reactive/Octokit.Reactive-Monotouch.csproj Octokit/Octokit-Mono.csproj Octokit/Octokit-MonoAndroid.csproj Octokit/Octokit-Monotouch.csproj Octokit/Octokit-Portable.csproj Octokit/Octokit-netcore45.csproj Octokit/Octokit.csproj
new UpdateMaintenanceRequest(), | ||
EnterpriseHelper.ManagementConsolePassword); | ||
|
||
Assert.NotNull(maintenance); |
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.
🔥 - the next assert will cover this
thanks for the comments. Ill revisit this soon with those comments in mind and see what a fresh perspective brings. I think I've already got a branch somewhere where I at least used the serializer to build the JSON string but from memory I still couldnt get it to be accepted in the request body and had to go with the URLEncoded approach. |
…e the initial/end state of maintenance mode
…e-maintenance # Conflicts: # Octokit.Reactive/Octokit.Reactive-Mono.csproj # Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj # Octokit.Reactive/Octokit.Reactive-Monotouch.csproj # Octokit/Octokit-Mono.csproj # Octokit/Octokit-MonoAndroid.csproj # Octokit/Octokit-Monotouch.csproj # Octokit/Octokit-Portable.csproj # Octokit/Octokit-netcore45.csproj
…izer can be used to serialize it. Remove MaintenanceDate class and just pass in the Date/string for when Still need to use UrlFormEncoding rather than json in the POST body though...
OK I think all comments have been addressed I've also changed the request object side of things so the structure matches what the API expects, and I can use The usage is now I still need to UrlFormEncode the request rather than have it in the request body, so Ive made a new class similar to |
…existing RequetParameters) and inherit from it in UpdateMaintenanceRequest
public void Dispose() | ||
{ | ||
// Ensure maintenance mode is restored to desired state | ||
EnterpriseHelper.SetMaintenanceMode(_connection, _initialState); |
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.
Why is this setting it to the exact same thing as the ctor?
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 a helper context class to be used via a using statement wrapping a test. So a test that sets maintenance ON, needs to ensure that maintenance was OFF to begin with and vice versa, hence the intial state and setting it in the ctor
.
But also we dont want to leave GHE in maintenance mode after a test (since that is pretty useless for any subsequent tests) so the destructor should be ensuring maintenance mode is OFF again. Which make me realise Im currently setting the intial state in destructor but actually should always be setting OFF 😀
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.
Which make me realise Im currently setting the intial state in destructor but actually should always be setting OFF 😄
👍
…ance OFF regardless of initial requested state
Rebased and assigned to @shiftkey for review |
Moving this to #2010 to see if we can actually land this |
This is the beginnings of adding support for #1024 the GitHub Enterprise Management Console API
To begin with Ive only added the Get/Set Maintenance Mode functions and the actual plumbing around how we can actually integrate with the management console API.
Im aware there are merge conflicts as I havent rebased to latest master yet. We are far from done with this PR so Ill do that in good time. For now I just want to get the [WIP] up to find out from @haacked and @shiftkey how far off track i've taken things :
Notes
This API is significantly different to all the "regular" github APIs. There are special requirements around connectivity and authentication and it also doesnt have fully fleshed out calls that take multiple parameters, but in more than one place simply have a single parameter that takes a json string specifying multiple parameters. To top it off it had some really strange handling around date formats and some other things.
Im definitely after guidance on some of the design decisions ive made...
Summary
Read below for large detail on the inner workings and design decisions I made, but in a nut-shell someone using octokit.net against a GitHub Enterprise server an now simply call the following methods on the same
GitHubClient
client they have already created for regular API access.Network access
Since octokit handles following redirects, we don't have any issues around handling the special ports of
8080
and8443
so we are good on this front and can simply target this API at the regular server address the user is passing into theGitHubClient
as octokit will follow redirects as neededNot as lucky on the Uri front though... The management console api calls must be targetted at
http://my.enterprise.server/setup/api
ie they can't have the/api/v3/
in the URL as all other calls do.Unfortunatley in the guts of octokit.net's
GitHubClient
andConnection
classes, this/api/v3/
is added to any custom URL specified by the user and saved in thebaseUri
of theConnection
object.Originally my implementation made it so that you had to declare a new/special
GitHubClient
using a new constructor essentially stating that you wanted to use it for management console access, and this essentially passed abool
through which prevented the/api/v3/
being put in the uri. But of course this then meant that all the other API clients on thisGitHubClient
wouldnt actually work... and similarly it meant that theclient.Enterprise.ManagementConsole
client wouldn't work on a "regular"GitHubClient
you were using to call the normal APIs.My latest iteration was actually exploiting behaviour of the dotnet
Uri
appending behaviour, where if an endpoint relative Uri starts with a leading forward slash, it actually causes any Uri on thebaseUri
to be stripped off. We've encountered the odd bug over time where inadvertently including these leading slashes on Urls in theApiUri
helper class has caused endpoints to not work on github enterprise, but now it seemed like this behaviour might actually help us here. Rather than just magially include the leading slashes on the management console Uris, i implemented a functionCorrectEndpointForManagementConsole(endpoint)
that is called in each management console client method, to add the leading slash if not already there. This does seem a bit hacky but really it is a fairly reliable way to allow a user to specify oneGitHubClient
object and credentials that work against the regular APIs on their enterprise instance, then when they use the ManagementConsole API on the same client, we manipulate the Uri via the endpoint (ie not manipulating the baseUri) and provide the management console password each time. Plus there is zero behavioral change for anyone using octokit against github.com (ie not an enterprise user). As an octokit contributor and enterprise user I felt like this has achieved good usability and is an acceptable code base compromise. At least it's a starting point to see how @shiftkey and others feel :)Authenticating
The management console has a special password to access it. For API use, this password is specified as the api key, however it appears it cant be specified in the "normal" way the user/password or OAUTH token can be (ie using our
Credentials
object which inserts it in a header in base64/UTF8 form). I was only able to get things successfully working, by having?api_key=MyPassword
on the end of the Uri (or potentially it would also work being embedded in the server namehttps://api_key:MyPassword@hostname
but that causes similar problems as above if we want to try and have the oneGitHubClient
object able to work with all APIsSo with the way i've implemented it on the end of the Url, it actually doesnt work too badly as it means the existing credentials provided on creating the
GitHubClient
can stay, and are used for regular API access, and every management console method takes amanagementConsolePassword
parameter. I actually like this as it's more inline with the seriousness of some of these management console activities - eg along the lines of "provide your password everytime you do these management related things"The irksome thing is that the password is essentiall in clear text on the end of the Url but im not sure if that's really any different to it being in the HTTP headers for regular calls anyway...
What the heck is up with parameters containing json strings?!!
The Set Maintenance Mode call takes a single
maintenance
parameter which is defined as follows:I tried and tried and I just could NOT get things working in the normal way where we have a request object, which gets serialized to json and put in the body of the HTTP Post. I had many incarnations providing a class with a public
Maintenance
parameter that then contained the c# object class with theEnabled
andWhen
parameters on it. When that didnt work I tried simply having a publicstring
Getter parameter that deserialized the class to json (didnt work), so then moved to constructing the json string in a more basic way - both normal format and fully escaped, I tried doing the parameter dictionary type approach like used in search requests, I tried the default content type andapplication/json
content types but in all cases github would throw a HTTP 400 Bad Request.Eventually I found that if I just specified the string in the url-form-encoded format of
maintenace="{"enabled":true, "when":"now"}"
it worked, so that's where I'm at at the moment. I plan to tidy up the json string to not be created manually and instead call a serializer but for now I have something working so didnt want to wait in pushing it up.If anyone is able to show me how to get this working in a more "normal" way for octokit where the request object is serialized using the builtin
SimpleJsonSerializer
in the normal pipeline, I would love to see what I missed bercause I feel like I tried so many attempts and still couldnt crack it without going the form encoded route.This is definitely important to sort out a strategy for, because the Settings endpoint on the management console is a real doozy containing literally 50+ items in a hierarchical json structure that all have to get packed into a single json string on a
settings
property in the API call. I am NOT looking forward to that endpoint!What the heck is up with the time handling?!!
The response to a scheduled maintenance mode event includes this field
"scheduled_time": "Tuesday, January 22 at 15 => 34 -0800"
In any other github API call ive encountered, the dates are in a lovely and easily parseable ISO 8601 format
In this case we not only have the the full day and month names (this will pose issues if github can run in other cutures/languages), the silly formatting like comma and
=>
the non standard timezone offset information (in dotnet land this should be-08:00
rather than-0800
but most importantly THE YEAR is missing! This plays havoc with attempting to actually parse this into a date. Now if we hardcode a culture likeen-US
we can get around the full day/month names, and we can "almost" useParseExact()
to match most of the formatting issues, except the lack of year means we cant useDateTimeOffset.ParseExact()
but if we useDateTime.ParseExact()
the time zone offset is not handled well.Given that the day of week and month are mentioned, we "could" potentially take an educated guess at the year, since eg if it says Tuesday the 12th April, we can figure out what YEAR in the future the 12th April will fall on a Tuesday and presume it to be this... then we could shove that year into the string. This is clearly getting ridiculous though (espcially because this field is coming back from the API and we want it to be deserialised properly in the Json deserializer) so I took the easy way out and simply made it a
string
!!!In runtime testing this behaviour it's even more odd because through the API I can scheduled maintenance for a date in 1/1/2017 and the API response
Sunday, January 1 at 20:00
indicates it really is that 2017 date - the fact it says Sunday is the confirmation. Yet the management console UI will show it as 1/1/2016 but in a scheduled status. If it really was 20-16 we should either now BE in maintenance mode (not scheduled) or it should reject the update since it's in the past. So im thinking even the management console UI itself is using this API call with the missing date and just assuming it's the current year.Now does it make sense to schedule maintenance for so far in the future? Not really! But given the API lets you do it, it's not really our place to limit this in any way. My hope is that this API call gets modified to include a properly parseable ISO 8601 formatted date like all other dates in the normal APIs, and the management console GUI fixed up to know the proper year as well 😀 In the meantime Im thinking we have to leave this field as a
string
because parsing it is too difficult and not 100% reliable.Other design decisions
The SetMaintenance method
when
parameter can handle "any" string that the mojombo/chronic ruby library can parse. This includes lots of humanised things like "next tuesday at 5pm" and "3pm on the monday before friday in 3 weeks time". Luckily for our api case we simply have to take whatever string the user says and pass it through. But ive also added specific suport to passing in aDateTimeOffset
object since that is more aligned to our c# nature, and also the specialnow
keyword as indicated by the API docs. To do this i created aMaintenanceDate
object used as theWhen
parameter on theUpdateMaintenanceRequest
object, and helper functionsMaintenanceDate.FromDateTimeOffset()
MaintenanceDate.FromChronicString()
andMaintenanceDate.Now()
There appear to be finite values of
off
on
andscheduled
so I made this an enum. The API docs dont definitively state the finite values though... This isnt too different to other enums we've implemented thoughadded to the powershell helper script and the
EnterpriseHelper
class, so the integration tests can runThat's about it for now. Please have a read and provide feedback... I'm looking at you @shiftkey 😀