Skip to content

Commit

Permalink
Redesign the task flow post Orlando release (#18)
Browse files Browse the repository at this point in the history
* - Rewrite the tasks to be run with per action checks, also to have some output on what the app is actually doing
- Add an option to specify timeout in the cli and config file

* - Bump go version

* - Add the timeout to config.sample.json

* - Build the linux version of the app only, this reduces build time

* Update README.md

Add disclaimer and clarify what this app does in the detail to prevent confusions
  • Loading branch information
0x111 authored Apr 19, 2020
1 parent 773e181 commit f58538f
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 28 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.12
- name: Set up Go 1.14
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.14
id: go

- name: Check out code into the Go module directory
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM golang as builder
ADD . /src
RUN cd /src && make
RUN cd /src && make linux_amd64

#-------
FROM chromedp/headless-shell
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

This app is here to help you with wakeing up your instance if needed.

----
**Disclaimer**: This app is not made to keep your instance awake. This app was created due to the amount of time it takes for you, to wake up your instance.

It takes you approximately three to four minutes (sometimes more), to wake up your instance up right now. 90% of this time, is spent with waiting for redirects, filling out the username and password, waiting for some more redirects and then pushing one button.

This app can be used, to reduce the time to do the manual steps required to wake up your instance. I do not condone keeping any instance awake just for the sake of it. This app only clicks the wakeup button if existing. This is not generating any actions that would keep your instance awake. It is simply emulating the manual tasks that you would do anyways if your instance would go to sleep. The app does not even work or does anything if your instance is already awake.

This is not a software to keep any instance awake. Please respect that! There were attempts, to make this a tool to keep it awake, like you can see in [#10](https://github.com/0x111/servicenow-instance-wakeup/issues/10) but this was rejected. Simply said, please do not categorize this app as something, that is jeopardizing the free PDI program. It has nothing to do with it.

----

All of you who work with dev instances, you know what is this about.
Dev instances expire after a specific time period.

Expand Down
3 changes: 2 additions & 1 deletion config.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"username": "developer@email",
"password": "password",
"headless": false,
"debug": false
"debug": false,
"timeout": 60
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module servicenow-instance-wakeup

go 1.14

require github.com/chromedp/chromedp v0.5.3
110 changes: 86 additions & 24 deletions servicenow-instance-wakeup.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"flag"
"fmt"
"github.com/chromedp/chromedp"
"io/ioutil"
"log"
Expand All @@ -12,22 +13,25 @@ import (
)

type User struct {
Username string `json:"username"`
Password string `json:"password"`
ChromeHeadless bool `json:"headless"`
Debug bool `json:"debug"`
Username string `json:"username"`
Password string `json:"password"`
ChromeHeadless bool `json:"headless"`
Debug bool `json:"debug"`
Timeout time.Duration `json:"timeout"`
}

func main() {
var err error
var configFile string
var timeout time.Duration
userDetails := &User{}

flag.StringVar(&userDetails.Username, "username", "", "write the username/email with which you are logging in to the developers account")
flag.StringVar(&userDetails.Password, "password", "", "write the password with which you are logging in to the developers account")
flag.BoolVar(&userDetails.ChromeHeadless, "headless", false, "bool, if we need headless mode with chrome or not, default:false")
flag.BoolVar(&userDetails.Debug, "debug", false, "bool, if you want debug output or not, default:false")
flag.StringVar(&configFile, "config", "", "Provide the config file name, it can be a relative path or a full path, e.g. /home/user/servicenow-config.json or just simply 'config.json'")
flag.DurationVar(&timeout, "timeout", 60, "Set the timeout after which the app should exit. This is a number in seconds, default:60")
flag.Parse()

// Read config into struct if exists
Expand Down Expand Up @@ -69,33 +73,91 @@ func main() {
)
defer cancel()

err = wakeUpInstance(ctx, userDetails.Username, userDetails.Password, timeout)

if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func wakeUpInstance(ctx context.Context, username string, password string, timeout time.Duration) error {
var cancel func()
// create a timeout
ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
ctx, cancel = context.WithTimeout(ctx, timeout*time.Second)
defer cancel()

// run task list
err = chromedp.Run(ctx, wakeUpInstance(userDetails.Username, userDetails.Password))
initialURL := "https://developer.servicenow.com/ssologin.do?relayState=%2Fdev.do%23%21%2Fhome"

if err != nil {
log.Fatal(err)
fmt.Printf("Navigating to the webpage: %s\n", initialURL)
// first navigate to the sso login page
if err := chromedp.Run(ctx, chromedp.Navigate(initialURL)); err != nil {
return fmt.Errorf("could not navigate to the SSO login page: %v", err)
} else {
fmt.Printf("Successfully navigated to the webpage...\n")
}

fmt.Printf("Searching for the .logo element...\n")
if err := chromedp.Run(ctx, chromedp.WaitVisible(`.logo`)); err != nil {
return fmt.Errorf("could not detect .logo element: %v", err)
} else {
fmt.Printf("Found .logo element\n")
}

fmt.Printf("Filling out the username field...\n")
if err := chromedp.Run(ctx, chromedp.SendKeys(`#username`, username, chromedp.ByID)); err != nil {
return fmt.Errorf("could not fill out the username: %v", err)
} else {
fmt.Printf("Filled username field with %s\n", username)
}
}

func wakeUpInstance(username string, password string) chromedp.Tasks {
return chromedp.Tasks{
// This is the url which you are redirected to
// after opening an inactive instance https://developer.servicenow.com/app.do#!/instance?wu=true
chromedp.Navigate(`https://developer.servicenow.com/ssologin.do?relayState=%2Fapp.do%23%21%2Finstance%3Fwu%3Dtrue`),
chromedp.WaitVisible(`.logo`),
chromedp.SendKeys(`#username`, username, chromedp.ByID),
chromedp.Click(`#usernameSubmitButton`, chromedp.ByID),
chromedp.WaitVisible(`#password`),
chromedp.SendKeys(`#password`, password, chromedp.ByID),
chromedp.Click(`#submitButton`, chromedp.ByID),
chromedp.WaitVisible(`#instanceWakeUpBtn`, chromedp.ByID),
chromedp.Click(`#instanceWakeUpBtn`, chromedp.ByID),
chromedp.WaitNotVisible(`#dp-instance-hib-overlay`, chromedp.ByID),
fmt.Printf("Clicking the next button...\n")
if err := chromedp.Run(ctx, chromedp.Click(`#usernameSubmitButton`, chromedp.ByID)); err != nil {
return fmt.Errorf("could not click the next button: %v", err)
} else {
fmt.Printf("Clicked Next button\n")
}

fmt.Printf("Searching for the password field...\n")
if err := chromedp.Run(ctx, chromedp.WaitVisible(`#password`)); err != nil {
return fmt.Errorf("could not detect password element: %v", err)
} else {
fmt.Printf("Found password field\n")
}

fmt.Printf("Filling out the password field...\n")
if err := chromedp.Run(ctx, chromedp.SendKeys(`#password`, password, chromedp.ByID)); err != nil {
return fmt.Errorf("could not fill out the password: %v", err)
} else {
fmt.Printf("Filled password field with your password ******\n")
}

fmt.Printf("Clicking the submit button...\n")
if err := chromedp.Run(ctx, chromedp.Click(`#submitButton`, chromedp.ByID)); err != nil {
return fmt.Errorf("could not click submit button: %v", err)
} else {
fmt.Printf("Clicked Submit button\n")
fmt.Printf("Login successful!\n")
}

fmt.Printf("Detecting the wakeup button element to determine if we are on the developer portal homepage...\n")
if err := chromedp.Run(ctx, chromedp.WaitVisible(`document.querySelector("body > dps-app").shadowRoot.querySelector("div > main > dps-home-auth").shadowRoot.querySelector("div > div > div.instance-widget > dps-instance-sidebar").shadowRoot.querySelector("div > div.dps-instance-sidebar-content.dps-instance-sidebar-content-instance-info > div.dps-instance-sidebar-content-btn-group > dps-button").shadowRoot.querySelector("button")`, chromedp.ByJSPath)); err != nil {
return fmt.Errorf("could not find shadow element (header status bar): %v", err)
} else {
fmt.Printf("Element found\n")
}

fmt.Printf("Sleep for a %d seconds for the render of the nodes...\n", 5)
time.Sleep(5 * time.Second)

var res int
if err := chromedp.Run(ctx, chromedp.EvaluateAsDevTools(`(function(){document.querySelector("body > dps-app").shadowRoot.querySelector("div > main > dps-home-auth").shadowRoot.querySelector("div > div > div.instance-widget > dps-instance-sidebar").shadowRoot.querySelector("div > div.dps-instance-sidebar-content.dps-instance-sidebar-content-instance-info > div.dps-instance-sidebar-content-btn-group > dps-button").shadowRoot.querySelector("button").click();return 1;})()`, &res)); err != nil {
return fmt.Errorf("could not click on shadow element (button): %v", err)
} else {
fmt.Println("Clicked on the Wakeup instance button! Exiting...")
}

return nil
}

// Read the config file if required and load the json to the struct
Expand Down

0 comments on commit f58538f

Please sign in to comment.