Part 1: Introduction
Today I decided to learn Go in the next 30 days and document every step of my journey. How well will I know Go in 30 days? That is a difficult question and by logging the steps, I can hopefully measure progress. How much time should I spend each day? I have a theory you will learn and retain far more information by studying and practicing every day compared to once in a while cram sessions. I also have another theory that studying right before bed helps to retain even more information. I can go into a mental zone where I am totally focused on my task/subject. Time disappears. This takes between 15 and 30 minutes to get into the zone. Therefore, I will set my goal at two hours of study, practice and code writing every evening, six days per week.
What resources will I use? I don’t know yet. I am a big fan of Pluralsight and at first glance; they have 20+ courses on Go. Today, I will pick three and just get started. I will also do the classic HelloWorld example to get something running. My initial thoughts are to start with Pluralsight for 30 minutes, studying language documentation for 30 minutes and then real coding for the rest of the time. I will copy and study examples from the Internet, including my favorite StackOverflow.
Once I get beyond the basic programming stuff, I plan to focus entirely on Go with Google Cloud Platform. This means writing tools and applications designed for GCP with a sub-focus of Containers, Cloud Run and Kubernetes.
This will be a running blog article. I plan to update this article every day with notes, information, results, etc. If you are interested in learning Go, follow along.
What is my objective? To learn Go well enough to write everyday programs for the cloud. If we look at various skill levels consisting of beginner, novice, professional, expert and architect, I hope to get to the professional level in 30 days. This means that my previous skills writing software in other languages will play a big part in getting there. Today I am a beginner, in a week I should be at the novice level. By the end of 30 days at the professional level for GCP. To get to the expert level will take many years (5 – 10) of writing, testing and deploying Go applications in the real world. No better time to start than today.
Resources:
- Pluralsight – Go: The Big Picture by Mike Van Sickle – 107 minutes – May 21, 2019
- Pluralsight – Go: Getting Started by Mike Van Sickle – 111 minutes – July 8, 2015 – First version
- Pluralsight – Go Fundamentals by Nigel Poulton – 216 minutes – June 8, 2015
These videos total 434 minutes or about 7 hours of training material over 30 days. This is about 15 minutes per day. However, when I watch a training video, I am constantly pausing the video, making notes, following references, etc. I often watch a section again. This can make 15 minutes of video take an hour to complete. My goal is not to complete a course, but to retain as much information as I can and be able to apply that new knowledge in my work.
Added Resources:
- YouTube – Golang’s Database/SQL In Action by Baron Schwartz – 48 minutes – October 24, 2018
- Pluralsight – Concurrent Programming with Go by Mike Van Sickle – 156 minutes – June 4, 2015
- Pluralsight – Go: Getting Started by Mike Van Sickle – 111 minutes – June 28, 2019 – Second version
- Pluralsight – Debugging Go Applications with Delve by Mike Van Sickle – 125 minutes – July 10, 2017
- Pluralsight – The Go CLI Playbook by Mike Van Sickle – 128 minutes – February 27, 2018
- Pluralsight – Building Better Go Web Apps with the Gorilla Toolkit by Mike Van Sickle – 262 minutes – August 28, 2015
This section will keep track of the additional resources I am studying. My goal is not to bounce around but to be methodical learning Go, one step at a time. I have no interest in cramming. I intend to learn Go and all its nuances and this will take a consistent everyday study pattern.
Part 2: Completed Results
July 14, 2019. I finally took the Pluralsight IQ for Go. I scored 154 which is Proficient. I achieved my goal of having a good understanding of the language. However, there were questions on this IQ test that I did not know the answers to and that I did not see in my studies. I will continue studying each one of these areas. During the past month, I wrote a lot of code – several thousand lines, published several articles that included Go source code and really tried to think in Go. The language is easy to write in and I am very comfortable writing code to interface with Google Cloud Platform. I plan to continue studying the more advanced topics of the language and retake the Pluralsight IQ exam later this year.
Part 3: Daily Study
Index
- Day #1 – June 11, 2019
- Day #2 – June 12, 2019
- Day #3 – June 13, 2019
- Day #4 – June 14, 2019
- Day #5 – June 15, 2019
- Day #6 – June 16, 2019
- Day #7 – June 17, 2019
- Day #8 – June 18, 2019
- Day #9 thru #19 – June 19-29, 2019
- Day #20 – June 30, 2019
- Day #21 – July 1, 2019
- Day #22 – July 2, 2019
- Day #23 – July 3, 2019
- Day #24 – July 4, 2019
- Day #25 – July 5, 2019
- Day #26 – July 6, 2019
- Day #27 – July 7, 2019
- Day #31 – July 11, 2019
- Exam Day – July 14, 2019
Day #1 – June 11, 2019
Try to start your training time with an objective. Sometimes this means changing your objective, but for me, this usually means I stick with it until I achieve my objective each day.
My goals for today are:
- Install Go on my Windows system.
- Create a Hello World console application, learn how to compile and run.
- Deploy a Hello World web application to Docker and then deploy to Cloud Run.
- Complete 60 minutes of video training.
Go Installation
I started by downloading and installing Go to my Windows 10 Professional system. Link. I installed to C:\go. Running go version
reports 1.12.5. Then I created a working directory on my system to keep track of everything I write organized by sub-folders for each day.
Hello World Application
Next, I searched the Internet for a Go Hello World example. I chose this one: link.
Copied the example program to “main.go”.
1 2 3 4 5 6 7 |
package main import "fmt" func main() { fmt.Printf("hello, world\n") } |
Next is to run the program.
1 |
go run main.go |
To build the program into a binary:
1 |
go build main.go |
Very simple steps. Reminds me of C programming; edit – compile – run. The source code reminds me of Python and C combined into a new language. I know both well, so this should be interesting.
Video Training
I will start with Nigel Poulton’s Go Fundamentals.
Go Fundaments Section 1
The first item that I learned from Nigel’s course is that Docker, etcd, and Kubernetes are written in Go. If I had known these little facts last year, I would have made Go my goal to master a year ago. So much is happening in our market it is hard to keep track. Very exciting times.
I like Nigel’s teaching style and course content. I have watched many of his courses. Nigel could probably motivate me to design an ice cube factory in Alaska (just kidding). Section 1 reviewed the course content and provided good motivation for learning Go.
Go Fundaments Section 2
Interesting Go Language Facts:
- Go was created at Google by Robert Griesemer, Rob Pike and Ken Thompson. Interesting. I knew and worked with Ken Thompson back in the early days of C and Unix.
- Rob Pike’s wife designed the Go Gopher mascot logo. Official Gopher graphics: link.
- Work started on Go in 2007. On Rob Pike’s Twitter account (@rob_pike) he tweeted September 21, 2007: link.
- Initial release on November 2009 as open source.
- Version 1 (go1) released on March 28, 2012.
- Started as a “systems” language. This includes operating systems, device drivers, etc.
- Like C but simpler.
- Garbage collection managed by Go.
- ~25 keywords.
- Cross Platform.
- Compiled language.
- Strongly typed.
- Supports type inference.
- Go is case sensitive.
- Packages: Exported names need a capital letter otherwise they are not accessible.
Go Fundaments Section 3
In the lesson “Setting up Our Workspace”, I understood the directory structure that Nigel suggests. However, I need to study further how this affects software development as I have my programs in their own directory in a different place on my file system.
I need to become very comfortable with the Go language tools. I am setting a soft goal of practicing with five command-line options each day. Today I have seen these:
- go run
- go build
- go install
- go version
- go env
- go help build (etc)
A more complete list from this document:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bug start a bug report build compile packages and dependencies clean remove object files and cached files doc show documentation for package or symbol env print Go environment information fix update packages to use new APIs fmt gofmt (reformat) package sources generate generate Go files by processing source get download and install packages and dependencies install compile and install packages and dependencies list list packages or modules mod module maintenance run compile and run Go program test test packages tool run specified go tool version print Go version vet report likely mistakes in packages |
Go Language Items:
- “package main”. By declaring package main, we are telling Go compiler to compile this code as a standalone executable and not as a shared library.
- The program entry point is
func main() {}
.func
is a keyword. - The function to print a line is
fmt.Println()
. This function is imported from the libraryfmt
(link). Other functions include Printf(), Scanf(), etc. Very similar to the C library. - The source code is available for just about everything. For example, the package fmt is available on GitHub (link). This will be an amazing resource to learn Go, by studying the actual library source code.
- Go is case sensitive.
- Packages: Exported names need a capital letter otherwise they are not accessible.
- Comments are like C: // or /* */. The first method is preferred.
Summary of Nigel Poulton’s Go Fundaments Section 1-3:
This is a good introduction to the Go language. You will quickly learn a bunch of points as shown above in my notes. This course looks good. Now to start the other courses for comparison.
Go: Getting Started
This course is by Mike Van Sickle. The table of contents appears different from Nigel Poulton’s course, so I am looking forward to several viewpoints on Go.
Go: Getting Started Section 1
Important Go Links:
- Go Home Page.
- Go Playground is a very cool environment for practicing Go code in a browser.
- Go Documentation.
- Go Packages.
- Go Project.
- Go Blog.
Go Tools:
- golint
Func requires the open brace on the same line.
This is supported:
1 2 |
func main() { } |
This will generate a compiler error:
1 2 3 |
func main() { } |
Important points:
- Functions are first class citizens and do not require being wrapped in a class like C# or Java.
- Go has a built-in function
println("Hello world")
that is useful for debugging. This function is not intended for production use. - If you do not reference an import package, Go will report an error.
Go supports constants:
1 2 3 4 5 6 7 8 9 |
package main const ( message = "Hello world" ) func main() { println(message) } |
Go supports variables. Note the difference between Go and C. In C we would declare the type first. In Go we declare the name first and then the type:
1 2 3 4 5 6 7 8 9 |
package main var ( message string = "Hello world" ) func main() { println(message) } |
Go supports an initialization function that runs on program startup. Public variables are initialized first and then the init() function is called.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main var ( message string ) func main() { println(message) } func init() { message = "Hello world" } |
Summary of Mike Van Sickle’s Go: Getting Started Section 1-2:
This is also a good introduction to Go presented differently. Mike’s starting point is to explore the Go community first, which provided me a wealth of valuable information. Just learning about the Go Playground was valuable. I would watch both Nigel’s Section 1-3 and Mike’s Section 1 in series. It does not matter which course you start with. Great information from both that go together well.
Go: The Big Picture
This course is by Mike Van Sickle. From the description, I expect to learn the What, When, Where and Why of the Go Language.
Go: The Big Picture Section 1-2
Go Language Characteristics:
- Strong, static type system.
- C-inspired syntax.
- Multi-paradigm: procedural; object-oriented.
- Garbage collected.
- Fully compiled.
- Rapid compilation.
- Single binary output.
Go Philosophy and Values:
- Simplicity.
- Network aware and concurrent apps.
- Out-of-the-box experience.
- Cross-platform.
- Backward compatibility.
Who is using Go in Production Applications?
- IBM
- Amazon
- Digital Ocean
- Twilio SendGrid
- CloudFlare
- Netflix
- Docker – written in Go
- Kubernetes
Comprehensive List of Go Users: link.
Go success stories from around the web: link.
This is an excellent section that helps me understand the business part of choosing Go. Good presentation. I will move this course to my mornings, when I have my business/marketing hat on and complete this course, one section each day over coffee.
Creating a Docker Image
I learned a lot about Go, but not too much about actually writing code. I want to finish my training day applying what I already know with Go. I will start with a Go web application, create a Docker container and test locally. Then I will deploy to Cloud Run.
I need an example Go web application, as I don’t know how to write one yet. I chose this one: link. This example just happens to be for Go and Cloud Run. The article is not complete, but close enough.
The application source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package main import ( "os" "log" "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello world") }) port := os.Getenv("PORT") if port == "" { port = "8080" } log.Fatal(http.ListenAndServe(":"+port, nil)) } |
The first step is to run this code in a Command Prompt:
1 |
go run main.go |
Connect with a web browser and go to http://localhost:8080/
You should see “Hello world”
Next step is to create a Dockerfile:
1 2 3 4 5 6 7 8 |
FROM golang WORKDIR /go/src/app COPY . . RUN go build -v -o /app . ENV PORT 8080 CMD ["/app"] |
Now build the Cloud Run image in Container Registry. Replace development-123456 with your Project ID:
1 |
gcloud builds submit --tag gcr.io/development-123456/go-helloworld |
Now deploy this image to Cloud Run:
1 2 3 4 |
gcloud beta run deploy go-helloworld ^ --region us-central1 ^ --image gcr.io/development-123456/go-helloworld ^ --allow-unauthenticated |
Mission accomplished. Learned some basic Go language stuff and deployed a simple Go web server to Cloud Run.
Total time spent today: three hours.
Day #2 – June 12, 2019
My goals for today are:
- Complete 30 minutes of video training.
- Find an OAuth2 example and port to run in a container and deploy to Cloud Run.
Last night, after completing my initial study of Go, I realized that the Go language is easy to learn. I feel that I can learn most of the Go language in one week. The real challenge is not the language, but the tools, libraries, and ecosystem that exists for Go. To become really effective with Go, I will need to spend a lot of time on everything that surrounds and supports the Go language. This will be the challenge.
I am a big fan of Qwiklabs. They have a couple of Go labs to practice with. However, by previewing these labs, I discovered a huge resource for learning Go for the Google Cloud Platform. Google has published a library of examples and Go is one of the languages:
Google Cloud Examples Package
On the Twitter hashtag #golang I found an interesting article My journey from Python to Golang. Very good article.
Go Fundaments Section 4 – Variables and Constants
Go Tips:
-
- Convert a string to a byte array:
[]byte("Hello World)
- Variables: Use var keyword if declaring at the package level. (What does this really mean?)
- Variables: Names must start with “_” or a letter.
- Go will initialize variables with a zero value.
- To get the type of a variable use
reflect.TypeOf(variable)
- Go supports determining the type:
var name = "Nigel"
Go set the type of name to string. - Package level variables are global.
- Shorthand to declare and initialize “:=” only works inside of functions.
name := "John"
This is called Short Assignment - Variables that are declared and not used at the function level will generate a compiler error.
- Pointers: Taking the address of a variable is just like C:
&variable
. fmt.Println()
prints pointers in hex0xc080281234
ptr := &variable
- Value (contents) of a pointer is just like C:
*ptr
. This is called De-referencing a pointer. - Constants: You cannot use Short Assignment with constants.
const x := 0
will generate a compiler error. - Environment:
os.Environ()
will return a list of environment variables. - Example:
fmt.Println("TMP directory:", os.Getenv("TMP"))
. - File open example:
in, err := os.Open(filename)
. - Lookup the keyword
defer
as indefer in.Close()
- Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. defer is often used where e.g. ensure and finally would be used in other languages.
- Convert a string to a byte array:
Summary of Nigel Poulton’s Go Fundaments Section 4:
This was a good section on variables and constants in Go. Very similar concepts to the C language. The video material was 25 minutes and I spent 60 minutes practicing in a Command Prompt.
Interesting Code Examples to Remember:
Print the environment variables. Note the range
keyword. I am using two different ways.
1 2 3 4 5 6 7 8 9 |
for _, env := range os.Environ() { fmt.Println("ENV:", env) } l := os.Environ() for _, env2 := range l { fmt.Println("ENV:", env2) } |
OAuth 2 Example in Go
Now to find an OAuth 2 example written in Go. I plan to improve whatever I find to provide OIDC Identity Tokens which can later be used for Cloud Run authentication.
I found this article with example code: Getting Started with OAuth2 in Go by Alex Pliutau. A copy and paste from his article did not work. He forgot to include some packages and other minor stuff that happens when you write an article.
Tomorrow I will include my code based upon Alex’s article.
Day #3 – June 13, 2019
My goals for today are:
- Complete 30 minutes of video training.
- Learn how to process
argv
and environment variables in a program. - Load and process JSON from a file.
- Understand how to use service accounts in Go.
- Write a simple program to read a Cloud Storage object.
My initial thoughts today. If you are a C language programmer, the Go language is easy to learn. Most of the language is the same. The libraries are very similar. The Go Standard Library reminds of me of the C Standard Library. Go has less “ad-hoc” stuff, where C and the libraries evolved over several decades. Go is cleaner. However, I am only in my third-day learning Go. Let’s see what I think after 30 days.
Go Getting Started – Section 2 – Data Types and Operators
Primitive Data Types:
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint64
- byte // alias for uint8
- rune // alias for int32 // represents a Unicode code point
- There are two float types
float32
andfloat64
. There is nofloat
keyword. - To explicitly specify a floating point number:
64.0
or64.
Notice the period (dot) at the end of 64. - Complex data type. See the example below. I am confused with complex() and complex32 and complex64. Research later.
Complex Data Type:
1 2 3 4 5 6 |
func main() { myComplex := complex(2,3) fmt.Println(myComplex) fmt.Println(real(myComplex)) fmt.Println(imag(myComplex)) } |
Example code (not optimized):
1 2 3 4 5 6 |
func main() { var myInt int myInt = 42 fmt.Println(myInt) } |
Example code (better):
1 2 3 4 5 |
func main() { myInt := 42 fmt.Println(myInt) } |
In the second example, we cleaned up the code by using a Go language feature called “Short Variable Declaration” (link and link).
Constants
- iota – This is similar to C’s enum. This simplifies definitions of incrementing numbers. See the example below.
Example iota:
1 2 3 4 5 6 7 8 9 10 11 |
func main() { const ( zero = iota one two ) fmt.Println(zero) fmt.Println(one) fmt.Println(two) } |
which outputs:
1 2 3 |
0 1 2 |
An interesting iota example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func main() { type ByteSize float64 const ( _ = iota // ignore first value by assigning to blank identifier KB ByteSize = 1 << (10 * iota) MB GB TB PB EB ZB YB ) fmt.Println(KB) fmt.Println(int(MB)) fmt.Println(int(GB)) fmt.Println(int64(TB)) fmt.Println(int64(PB)) fmt.Println(int64(EB)) fmt.Println(ZB) // Too big for int64 fmt.Println(YB) // Too big for int64 } |
which outputs:
1 2 3 4 5 6 7 8 |
1024 1048576 1073741824 1099511627776 1125899906842624 1152921504606846976 1.1805916207174113e+21 1.2089258196146292e+24 |
Collections:
- Array
- Arrays have a fixed size.
- Example:
myArray := [3]int{}
myArray[0] = 42
- You can print the contents of an array:
fmt.Println(myArray)
- You can get the length of an array:
len(myArray)
- Another method to initialize an array:
myArray := [3]int {42, 27, 99}
- Automatically size the array with initializers:
myArray := [...]int {42, 27, 99}
- Slice
- A Slice represents a subset of an array.
- Example:
mySlice := myArray[:]
- Example:
mySlice := myArray[1:]
- Example:
mySlice := myArray[:3]
- Changing the array changes the slice and any other slices.
- Changing the slice changes the array and any other slices.
- Initialize an array or slice with a fixed size:
myArray := make([]int, 100)
- Map
- Holds multiple values by customizable keys.
- Both the key and the value can be arbitrary data types.
Map Example:
1 2 3 4 5 6 7 |
func main() { myMap := make(map[string]string) fmt.Println(myMap) myMap["Firstname"] = "John" fmt.Println(myMap) fmt.Println(myMap["Firstname"]) } |
which outputs:
1 2 3 |
map[] map[Firstname:John] John |
Operators:
Go supports the usual C operators (+-*/%). Go also supports the postfix operators (x++) but not the prefix operators (++x). Also, these types of operators must be on their own line. Example myFunc(x++)
is not legal.
Summary of Go Getting Started – Section 2 – Data Types and Operators
This was a good section. Mike goes faster than Nigel does, which I prefer as this is still basic programming information. The key areas to note for the Go language are arrays, slices, and maps. The syntax is different compared to other languages, so this is just memorization work. 24 minutes of video took me more than an hour. I spent a lot of time in the Go Playground.
I will now pause the study part for a few hours and write my first article on Google Cloud and Go. This will be an article about Service Accounts and how to load the JSON file from Google Cloud Storage. I will use more advanced security techniques where the service account has no permissions. The security is managed by Google’s new Identity feature. I wrote about these features for my article on Cloud Run Identity. I will continue this article once the new article is published later today.
Google Cloud – Go – Identity Based Access Control
Article published including source code on GitHub. The Go language is easy to write programs with. I wrote a real program that uses security accounts to access data. Link to article.
Today’s Summary
I did not keep track of my time today. I spent about an hour on video training and practice. Then I started writing my article, researching, etc. I think the total time to research, write the code, test and write the article was two to three hours. I was mentally tired last night when I stopped.
Day #4 – June 14, 2019
I am not sure where I had the impression that learning Go would take a lot of effort. After just three days and maybe six hours of study, I could write a full program in Go that did something useful with service accounts and Cloud Storage. However, when judging skills, it is not what you know that matters; it is what you don’t know that comes back to bite you in programming.
I will continue with the video courses and learn the syntax and grammar working on difficult areas such as Interfaces. In reviewing Pluralsight’s courses on Go, I discovered another course on gRPC which has a section on Go. I have not worked with gRPC, so I am adding this course to my Go training starting today. This course is Enhancing Application Communication with gRPC by Michael Van Sickle.
Enhancing Application Communication with gRPC – Section 1-2
This course is by Michael Van Sickle. Although gRPC is not really related to Go, it fits into my objective of learning Go with a focus on Google Cloud Platform. gRPC is a common technology at Google and one I have been curious about as I write a lot of software that directly calls the REST APIs. I will learn how to use gRPC in Go which will help me improve my understanding of the Go ecosystem.
Background of gRPC
- Stubby – Developed at Google. Microservice Based; Cross-Platform; Scalable; Precursor to gRPC.
- gRPC – Developed at Google. Cross-Platform; Scalable, Streaming; Free and Open; Pluggable; Layered.
- gRPC – Fast and Efficient Protocol; Strongly Typed Messaging.
Tour of gRPC
Go Getting Started – Section 3 – Branching and Looping
Branches:
- If statements. Parenthesis are not required but do work. Examples:
if x == 12 {
if (x == 12) {
- Single line if statements are supported:
if x == 12 { fmt.Println("Match") }
- The left brace must be on the same line as the if statement. If moved to the next line, the compiler will report an error.
Switch:
Notice the three styles (types) of switch statements. The first one compares strings, the second compares ints, the third compares boolean conditions. The third one does not have a variable in the switch statement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<span id="selectionBoundary_1560553908638_6644434132698771" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span><span id="selectionBoundary_1560553908895_6169815762098787" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span><span id="selectionBoundary_1560553908899_7978444634431023" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span><span id="selectionBoundary_1560553908978_29205860156729635" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span>func main() { os := runtime.GOOS fmt.Print("Go runs on ") switch os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.\n", os) } x := 12 switch x { case 1: fmt.Println("One") case 12: fmt.Println("Twelve") } x = 5 switch { case x < 12: fmt.Println("Less than 12") case x > 11: fmt.Println("Greater than 11") } } |
Loops:
- For loops are just like C:
for(i:=0; i < 5; i++) {
- For look like the C while:
for {
Console Input
var option string
fmt.Scanln(&option)
Summary of Go Getting Started – Section 3 – Branching and Looping
This was a good section. Mike did not introduce many new features. Almost everything matches the C language. Except for the :=
and variable declarations you might think the code is C.
The next section in Go Fundamentals is on Conditions which matches today’s lesson. I will take that tomorrow to reinforce everything.
Google Cloud SQL + Google Cloud Run
I have been planning an article on Cloud Run + Cloud SQL for a month. I will spend the rest of this evening learning how to program databases in Go. I will start with this video, which is very good:
My first MySQL database program in Go. This code connects to the SQL Proxy running on my desktop to Cloud SQL. The database has a copy of an old WordPress website, so I have some data to query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package main import "fmt" import "log" import "database/sql" import _ "github.com/go-sql-driver/mysql" func main() { fmt.Println("Hello MySQL World") db, err := sql.Open("mysql", "root:password@/wordpress") defer db.Close() if err != nil { fmt.Println(err.Error()) return } err = db.Ping() if err != nil { fmt.Println(err.Error()) return } type Tag struct { username string `json:username` email string `json:email` } var tag Tag err = db.QueryRow("SELECT user_login, user_email FROM wp_users where user_login = ?", "username").Scan(&tag.username, &tag.email) if err != nil { fmt.Println(err.Error()) return } log.Println("Username:", tag.username) log.Println("Email:", tag.email) } |
I also started to practice with JSON. This example will take a map and write it out as JSON to os.Stdout
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package main import ( "encoding/json" "fmt" "os" ) func main() { fmt.Println("Hello World") m := map[string]string{"name": "John", "last": "Smith"} enc := json.NewEncoder(os.Stdout) enc.Encode(m) } |
Good day studying overall. The Go language and libraries are very easy to work with provided you have Google to search for stuff.
Day #5 – June 15, 2019
Today I woke up early and started training at 6 AM. My plan is to finish three hours of studying before anyone else wakes up and then enjoy the great weather we are having in Seattle.
I started by finishing the course Go: The Big Picture by Mike Van Sickle. This is a great course to give you an overview of the Go language and the benefits of using Go. He is non-biased which is refreshing. As a beginning Go developer, watch Section 4 “What is it Like to use Go?” – great code demonstrations.
Mike recommends the following courses. I am already watching the first one:
- Go: Getting Started – 101 minutes
- Concurrent Programming with Go – 156 minutes
- Creating Web Applications with Go – 287 minutes
- Creating Well-tested Applications in Go – 228 minutes
Let’s review where I am at with the training courses:
- Go: The Big Picture – Completed 6/15/19
- Go: Getting Started – In Progress – 59 minutes of 111 minutes – 53%.
- Go Fundamentals – In Progress 78 minutes of 216 minutes – 36%
Go progress. I am taking my time and slowly going thru each course. I am practicing over and over the simple items such as grammar. I want a good foundation with no bad habits.
Writing in Go is easy for me now. My big challenge is that I am not familiar with the packages/libraries. That is a big item that I must complete. I plan to start with the Go Package page at the top and write some code using each library (one at a time). Within a few weeks, I will know the packages/libraries much better. To be efficient and productive in a language means “don’t reinvent the wheel”. Use existing code and libraries where applicable.
Go Fundaments – Section 4 – Conditionals
Nigel makes a mistake in the lesson “if in Practice”. He is comparing strings instead of numbers. His code works by accident instead of by design. Solution: remove the quotes around the numbers.
One of the goals of the Go language is simplicity. Based upon this, I don’t see why the Go language creators added this syntax:
1 |
if <simple statement> ; <Boolean expression> { |
The “simple statement” is really unnecessary, and I think creates a more complex if statement. Add to this that any variables defined in the if statement are local and go out of scope after the code block executes. My opinion, move the simple statement to its own line before the if statement. This allows for comments, etc that might be needed to clarify what you are actually coding.
Here is an example (I made this up):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import ( "fmt" ) func get_number() int { return 0 } func main() { if x := get_number(); x == 0 { fmt.Println("X is 0") } // x is undefined here } |
I think the line if x := get_number(); x == 0 {
is not improved by having x := get_number();
in the if statement. Move the initialization of x to its own line.
A new feature that exists in Go but not C is the “fallthrough” statement. In C switch case statements fallthrough by default unless there is a break statement. In Go, break statements are unnecessary and you must specify fallthrough to continue executing the next case statement.
Concurrent Programming with Go – Section 1 – Concurrency Patterns
This section is a simple review of the various concurrency models:
- Concurrency vs. Parallelism
- Processor Threads
- Events
- Callbacks and Promises
- Communicating Sequential Processes
Concurrency in Go:
- No Thread Primitives
- Goroutines
- Channels
Concurrent Programming with Go – Section 2 – Goroutines
Look into GOMAXPROCS as I think this has changed since Mike wrote the course in 2015.
I completed the first two sections of this course. Easy to understand how to implement goroutines. Section 2 had lots of demos and I will now spend the next hour reproducing sections of each demo for practice.
I want to study the documentation for the following Go packages:
- os
- time
- strings
- strconv
- encoding/csv
Good morning studying. I put more time in this morning that I had planned. I started at 6 AM and finished at 10 AM. I am now very comfortable writing Go programs. I have no problems reading other’s work.
Day #6 – June 16, 2019
Today my objective is to watch some YouTube videos on Go and complete the Go: Getting Started course. Then I will work on writing code for my article on Google Cloud Run + Cloud SQL.
I started with this video by Robert Pike: “why Golang is Successful by Creator of Golang Rob-pike”. Great video and I recommend watching Robert’s presentation after you have learned the basics of Go. I then watched “Brad Fitzpatrick Go 1 11 and beyond“. Brad can take a serious topic and make people laugh. Great presentation.
Go: Getting Started Section 4 – Functions
Variadic Functions accept any number of like arguments.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main import "fmt" func main() { sayHello("Hello", "Go", "from", "Plurasight") } func sayHello(messages ...string) { for _, message := range messages { fmt.Println(message) } } |
Named Return Values. I like this feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package main import "fmt" func main() { len := sayHello("Hello", "Go", "from", "Plurasight") fmt.Println(len) } func sayHello(messages ...string) (items int) { for _, message := range messages { fmt.Println(message) } items = len(messages) return } |
Go: Getting Started Section 5 – Object-oriented Programming
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package main import "fmt" func main() { foo := myStruct{} foo.myField = "bar" fmt.Println(foo) fmt.Println(foo.myField) } type myStruct struct { myField string } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import "fmt" func main() { foo := new(myStruct) foo.myField = "bar" fmt.Println(foo) fmt.Println(*foo) fmt.Println(foo.myField) } type myStruct struct { myField string } |
Outputs:
1 2 3 |
&{bar} {bar} bar |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package main import "fmt" func main() { foo := &myStruct{"bar"} fmt.Println(foo) fmt.Println(*foo) fmt.Println(foo.myField) } type myStruct struct { myField string } |
Outputs:
1 2 3 |
&{bar} {bar} bar |
In C++ taking the address of a local variable (&myStruct above) is a sure way to have a bug and probably a program crash. This is OK in Go.
Constructor Functions
I found constructor functions to be more of a hack (afterthought) than part of the design. Notice in the following code how you must call a function to initialize the struct instead of calling the struct itself (which you would do in other languages)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package main import "fmt" func main() { foo := newMyStruct() foo.myMap["bar"] = "baz" fmt.Println(foo) fmt.Println(*foo) fmt.Println(foo.myMap) } type myStruct struct { myMap map[string]string } func newMyStruct() (*myStruct) { result := myStruct{} result.myMap = map[string]string{} return &result } |
Methods
I have seen this new function specification in other source code, but I did not understand what I was looking at. In the code below look at the line func (mp *messagePrinter) printMessage() {
. The text after the func
did not make sense to me. Now add a return type to the declaration and I was confused even more. Now I understand.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package main import ( "fmt" ) func main() { mp := messagePrinter{"foo"} mp.printMessage() } type messagePrinter struct { message string } <span id="selectionBoundary_1560712646159_8024181044616856" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span>func (mp *messagePrinter) printMessage() {<span id="selectionBoundary_1560712646159_7590427586857451" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span> fmt.Println(mp.message) } |
Go: Getting Started Section 6 – Asynchronous Programming
Yesterday, I started the new course “Concurrent Programming with Go” which covered this section but much deeper. See my notes from yesterday.
Summary Go: Getting Started
This is a great course that provides beginner level training for the Go language. Michael Van Sickle is a good author, and this course is well worth two hours spread out with practice over several days – I spent six days on this course.
I have now completed two courses on Go. These courses are beginner level, but I now feel very comfortable reading other people’s Go code and writing my own code. I will continue and dig much deeper into the Go language and ecosystem.
Let’s review where I am at with the training courses:
- Go: The Big Picture – 107 minutes – Completed 6/15/19
- Go: Getting Started – 111 minutes – Completed 6/16/19
- Go Fundamentals – In Progress 107 minutes of 216 minutes – 49%
- Concurrent Programming with Go – In Progress 68 minutes of 156 minutes – 43%
Day #7 – June 17, 2019
No study today.
Day #8 – June 18, 2019
Today I gave myself a tough challenge. Write an OAuth program in Go that does not use any OAuth libraries. Success is determined by calling a Cloud Run service that requires authentication. This program is not yet finished but when it is ready, I will publish as a separate article.
Objectives:
- Write in Go.
- Do not use any OAuth libraries.
- Save the OAuth credentials in a file for later use. This provides the ability to authenticate once and save the Refresh Token.
- Develop the code for an “installed app”. This means that the OAuth completes via a built-in web server.
- Port the final code to be a package that can be added to my own tools to provide Google authentication.
- Note: I am using a web server that I wrote in Python previously for this project. The code is in my GitHub repository. I want to see what issues exist when calling a Python program from Go. There are some tasks that Python is really good at and I want to use both languages in some projects.
- Call a Cloud Run endpoint, passing the OAuth Identity Token in the Authorization header.
The code so far, which works by the way. I just need to document and clean up the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
package main // https://github.com/kirinlabs/HttpRequest import ( "encoding/json" "flag" "fmt" "io/ioutil" "os" "os/exec" "time" "github.com/kirinlabs/HttpRequest" ) var SavedUserCredentials = "user_credentials.json" var SCOPE = "https://www.googleapis.com/auth/userinfo.email" var ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth" var CHROME = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" type ClientSecrets struct { Installed struct { ClientID string `json:"client_id"` ProjectID string `json:"project_id"` AuthURI string `json:"auth_uri"` TokenURI string `json:"token_uri"` AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` ClientSecret string `json:"client_secret"` RedirectUris []string `json:"redirect_uris"` } `json:"installed"` } type UserCredentials struct { ClientID string `json:"client_id"` ClientSecret string `json:"client_secret"` RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` Type string `json:"type"` // The following two fields are option and exist after authentication AccessToken string `json:"access_token"` IDToken string `json:"id_token"` Email string `json:"email"` ExpiresAt int64 `json:"expires_at"` } type OAuthTokens struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` TokenType string `json:"token_type"` IDToken string `json:"id_token"` } func readCredentials(filename string) ([]byte, error) { in, err := os.Open(filename) if err != nil { return []byte(""), err } defer in.Close() b, err := ioutil.ReadAll(in) return b, err } func loadClientSecrets(filename string) (ClientSecrets, error) { var secrets ClientSecrets data, err := readCredentials(filename) if err != nil { return secrets, err } // fmt.Println(string(data)) err = json.Unmarshal(data, &secrets) if err != nil { fmt.Println("Error: Cannot unmarshal JSON: ", err) return secrets, err } // fmt.Println("ClientID:", secrets.Installed.ClientID) return secrets, err } func loadUserCredentials(filename string) (UserCredentials, error) { var secrets UserCredentials data, err := readCredentials(filename) if err != nil { return secrets, err } // fmt.Println(string(data)) err = json.Unmarshal(data, &secrets) if err != nil { fmt.Println("Error: Cannot unmarshal JSON: ", err) return secrets, err } // fmt.Println("ClientID:", secrets.ClientID) return secrets, err } func saveUserCredentials(filename string, creds UserCredentials) error { fmt.Println("Save Credentials to:", filename) // fmt.Println("Creds:", creds) j, err := json.MarshalIndent(creds, "", " ") if err != nil { fmt.Println("Error: Cannot marshall JSON:", err) return err } // err = ioutil.WriteFile(filename + ".test", j, 0644) err = ioutil.WriteFile(filename, j, 0644) if err != nil { fmt.Println(err) return err } return nil } func fileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() } func debug_PrintUserCredentials(creds UserCredentials) { fmt.Println("************************************************************") fmt.Println("ClientID:", creds.ClientID) fmt.Println("ClientSecret:", creds.ClientSecret) fmt.Println("RefreshToken:", creds.RefreshToken) fmt.Println("Scope:", creds.Scope) fmt.Println("Type:", creds.Type) fmt.Println("AccessToken:", creds.AccessToken) fmt.Println("IDToken:", creds.IDToken) fmt.Println("ExpiresAt:", creds.ExpiresAt) fmt.Println("Expires At:", time.Unix(creds.ExpiresAt, 0)) var t time.Time = time.Unix(creds.ExpiresAt, 0) var expires_in int64 = 0 if time.Now().Before(t) { expires_in = int64(creds.ExpiresAt) - int64(time.Now().UTC().Unix()) fmt.Println("Expires In:", expires_in) } else { fmt.Println("Expires In: Expired") } fmt.Println("************************************************************") } func doRefresh(filename string) (string, string, bool) { endpoint := "https://www.googleapis.com/oauth2/v4/token" creds, err := loadUserCredentials(filename) if err != nil { fmt.Println(err) return "", "", false } // debug_PrintUserCredentials(creds) // We want an access token that is good for a while. // Brand new tokens are valid for 3600 seconds // For testing require 15 minutes or 900 seconds var t time.Time = time.Unix(creds.ExpiresAt - (15 * 60), 0) // fmt.Println(t) // fmt.Println(time.Now()) if time.Now().Before(t) { fmt.Println("Saved Token OK") return creds.AccessToken, creds.IDToken, true } fmt.Println("Must Refresh Token") // return content := "client_id=" + creds.ClientID + "&" content += "client_secret=" + creds.ClientSecret + "&" content += "grant_type=refresh_token&" content += "refresh_token=" + creds.RefreshToken // fmt.Println("Content:", content) req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded"}) res, err := req.Post(endpoint, content) if err != nil { fmt.Println("Error: ", err) return "", "", false } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return "", "", false } // fmt.Println("Body Length: ", len(string(body))) // fmt.Println("Body: ", string(body)) var tokens OAuthTokens err = json.Unmarshal(body, &tokens) if err != nil { fmt.Println("Error: Cannot unmarshal JSON: ", err) return "", "", false } // fmt.Println("time.Now(): ", time.Now().UTC().Unix()) // fmt.Println(time.Now().String()) var expires_at int64 = int64(time.Now().UTC().Unix()) + int64(tokens.ExpiresIn) // fmt.Println("**********") // fmt.Println("Expires At:", time.Unix(expires_at, 0)) /* fmt.Println("AccessToken:", tokens.AccessToken) fmt.Println("ExpiresIn:", tokens.ExpiresIn) fmt.Println("ExpiresAt:", expires_at) fmt.Println("Scope:", tokens.Scope) fmt.Println("TokenType:", tokens.TokenType) fmt.Println("IDToken:", tokens.IDToken) */ creds.AccessToken = tokens.AccessToken creds.IDToken = tokens.IDToken creds.ExpiresAt = expires_at email, err := get_email_address(tokens.AccessToken) if err == nil { fmt.Println("Email:", email) creds.Email = email } err = saveUserCredentials(filename, creds) if err != nil { fmt.Println("Error: Cannot save user credentials: ", err) return "", "", false } return creds.AccessToken, creds.IDToken, true } func debug_displayAccessToken(accessToken string) { endpoint := "https://www.googleapis.com/oauth2/v3/tokeninfo" req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Authorization": "Bearer " + accessToken}) res, err := req.Get(endpoint) if err != nil { fmt.Println("Error: ", err) return } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return } fmt.Println(string(body)) } func debug_displayUserInfo(accessToken string) { endpoint := "https://www.googleapis.com/oauth2/v3/userinfo" req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Authorization": "Bearer " + accessToken}) res, err := req.Get(endpoint) if err != nil { fmt.Println("Error: ", err) return } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return } fmt.Println(string(body)) } func debug_displayIDToken(accessToken, idToken string) { endpoint := "https://www.googleapis.com/oauth2/v3/tokeninfo" endpoint += "?id_token=" + idToken req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Authorization": "Bearer " + accessToken}) res, err := req.Get(endpoint) if err != nil { fmt.Println("Error: ", err) return } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return } fmt.Println(string(body)) } func call_cloud_run_endpoint(url, id_token string) { endpoint := url fmt.Println("URL:", endpoint) fmt.Println("ID:", id_token) req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Authorization": "Bearer " + id_token}) res, err := req.Get(endpoint) if err != nil { fmt.Println("Error: ", err) return } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return } fmt.Println(len(body)) fmt.Println(string(body)) } func get_email_address(accessToken string) (string, error) { type Access_Token struct { Azp string `json:"azp"` Aud string `json:"aud"` Sub string `json:"sub"` Scope string `json:"scope"` Exp string `json:"exp"` Expires_in string `json:"expires_in"` Email string `json:"email"` Email_verified string `json:"email_verified"` Access_type string `json:"access_type"` } //************************************************************ // //************************************************************ endpoint := "https://www.googleapis.com/oauth2/v3/tokeninfo" req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Authorization": "Bearer " + accessToken}) //************************************************************ // //************************************************************ res, err := req.Get(endpoint) if err != nil { fmt.Println("Error: ", err) return "", err } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return "", err } //************************************************************ // //************************************************************ var tokens Access_Token fmt.Println("Valid:", json.Valid(body)) err = json.Unmarshal(body, &tokens) if err != nil { fmt.Println("Error: Cannot unmarshal JSON: ", err) return "", err } return tokens.Email, nil } func main() { // fmt.Println("Hello world") //************************************************************ // //************************************************************ flag_auth := flag.Bool("auth", false, "Authenticate ignoring user_credentials.json") flag_login := flag.String("login", "", "Specify an email address as a login hint") flag.Parse() //************************************************************ // //************************************************************ fmt.Println("Auth:", *flag_auth) fmt.Println("Login:", *flag_login) if *flag_auth == false { if fileExists(SavedUserCredentials) { accessToken, idToken, valid := doRefresh(SavedUserCredentials) if valid == true { fmt.Println("Access Token: ", accessToken) fmt.Println("ID Token: ", idToken) // debug_displayAccessToken(accessToken) // debug_displayUserInfo(accessToken) debug_displayIDToken(accessToken, idToken) url := "https://cloudrun-go-test.a.run.app/" call_cloud_run_endpoint(url, idToken) return } } } // secrets, err := loadClientSecrets("c:/config/client_secrets.json") secrets, err := loadClientSecrets("c:/config/client_secrets_mystic_advice.json") if err != nil { fmt.Println(err) return } cmd := exec.Command("python", "webserver.py") err = cmd.Start() if err != nil { fmt.Println(err) return } //************************************************************ url := ENDPOINT url += "?client_id=" + secrets.Installed.ClientID url += "&response_type=code" url += "&scope=" + SCOPE url += "&access_type=offline" if len(*flag_login) != 0 { url += "&login_hint=" + *flag_login } url += "&redirect_uri=http://localhost:9000" //************************************************************ cmd = exec.Command(CHROME, url) err = cmd.Start() if err != nil { fmt.Println(err) return } fmt.Println("Chrome running") //************************************************************ // Start the web server // // FIX: This is coded in Python. //************************************************************ fmt.Println("Web server starting") out, err := exec.Command("python", "webserver.py").Output() if err != nil { fmt.Println(err) return } if len(out) == 0 { fmt.Println("Error: Missing OAuth2 Code") return } fmt.Println("OAuth2 Code:", string(out)) AUTH_CODE := string(out) //************************************************************ content := "client_id=" + secrets.Installed.ClientID content += "&client_secret=" + secrets.Installed.ClientSecret content += "&code=" + AUTH_CODE content += "&redirect_uri=http://localhost:9000" content += "&grant_type=authorization_code" //************************************************************ endpoint := "https://www.googleapis.com/oauth2/v4/token" req := HttpRequest.NewRequest() req.SetHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded"}) res, err := req.Post(endpoint, content) if err != nil { fmt.Println("Error: ", err) return } body, err := res.Body() if err != nil { fmt.Println("Error: ", err) return } //************************************************************ // //************************************************************ var tokens OAuthTokens err = json.Unmarshal(body, &tokens) if err != nil { fmt.Println("Error: Cannot unmarshal JSON: ", err) return } //************************************************************ // //************************************************************ var expires_at int64 = int64(time.Now().UTC().Unix()) + int64(tokens.ExpiresIn) var creds UserCredentials creds.ClientID = secrets.Installed.ClientID creds.ClientSecret = secrets.Installed.ClientSecret creds.RefreshToken = tokens.RefreshToken creds.Scope = tokens.Scope creds.Type = tokens.TokenType creds.AccessToken = tokens.AccessToken creds.IDToken = tokens.IDToken creds.ExpiresAt = expires_at //************************************************************ // //************************************************************ email, err := get_email_address(creds.AccessToken) if err == nil { fmt.Println("Email:", email) creds.Email = email } //************************************************************ // //************************************************************ err = saveUserCredentials(SavedUserCredentials, creds) if err != nil { fmt.Println("Error: Cannot save user credentials: ", err) return } //************************************************************ // //************************************************************ // debug_displayAccessToken(creds.AccessToken) // debug_displayUserInfo(creds.AccessToken) debug_displayIDToken(creds.AccessToken, creds.IDToken) url = "https://cloudrun-go-test.a.run.app<span id="selectionBoundary_1560964468753_9669019667969891" class="rangySelectionBoundary" style="line-height: 0; display: none;"></span>/" call_cloud_run_endpoint(url, creds.IDToken) } |
Day #9 thru #19 – June 19 – June 29, 2019
I decided to write a major program in Go. Source code released today (June 29) on GitHub. This program provides a CLI interface to the Google Cloud Shell. This program supports authentication, SSH, SFTP, SSH Key Pairs, launching Putty, uploading files, downloading files, and remote command execution. Almost everything you need to make Cloud Shell part of your development environment.
This program was a lot of work. Not only am I learning the Go language, but I had to work with some new “alpha” level Google interfaces to Google Cloud Shell. Documentation is short on details in some areas. I worked on this program almost every evening for a couple of hours except for June 25, when I did a presentation on Google Cloud Run at the Google GDG in Portland Oregon.
Google Cloud Shell CLI Written in Go
Day #20 – June 30, 2019
No study today. I made several improvements for my Cloud Shell CLI to support Linux.
Day #21 – July 1, 2019
Michael Van Sickle released a rewritten course about Go on June 28, 2019: Go: Getting Started. I really liked the first version and his new course is twice as long. Now that I have written a few thousand lines of Go code and published two programs to GitHub, I want to go back to the beginning and study the basics again. I now have a new perspective on the Go language (which I really like) and I want to polish my technical knowledge. I also plan to Google search for a Go language interview questions list to see what is expected of Go programmers. This should provide me with a list of specific points to study further. In 10 days I plan to take the Pluralsight Go Assessment test and it is time to get ready.
Go: Getting Started Section 1 – 3 (New Course)
Important features of Go:
- Fast Compilation
- Fully Compiled
- Strongly Typed
- Concurrent by Default
- Garbage Collected
- Simplicity as a Core Value
Goal: Do more research on each item to understand the value proposition of Go and contrast with other languages that I know well such as C, C++, C#, JavaScript, and Python.
A feature of Go that created a bug in my program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package main import ( "fmt" ) func main() { x := 1 fmt.Printf("x = %d\n", x) { x := 2 fmt.Printf("x = %d\n", x) } fmt.Printf("x = %d\n", x) } |
Output:
1 2 3 |
x = 1 x = 2 x = 1 |
In this simplified example, I used :=
instead of =
because of a bad habit. The end result is that x
did not retrain the value that I expected. Note that in the real code this was part of an if
statement. Keep this in mind when using the feature :=
over declaring variables using var
statements. Here is a link to this code on play.golang.org
Today I completed the first three sections of Michael’s new course. I am very glad that I did. Excellent review. Also today, Jeremy Morgan (@JeremyCMorgan) send out a tweet about WSL and Visual Studio Code Remote (link to article). In Michael’s course, he uses the same editor. This started me on two paths. 1) to install WSL to use for Go development on Linux and 2) to learn VSCode for Go programming. I setup WSL on my laptop, which was very easy, then installed the Google Cloud SDK into Linux, setup VSCode, etc. Tomorrow I am hoping to learn the “remote” features of VSCode. Very interesting day.
Day #22 – July 2, 2019
Go: Getting Started Section 4 – Working with Primitive Data Types
Good section with an overview of the basic data types. I practiced extensively with pointers today. Go pointers are very similar to C/C++ with some limitations. Go does not support pointer indexing, incrementing, decrementing, etc. Since Go memory is managed and garbage collected, you are limited to what you can do. However, the designers left enough features in Go to make pointers useful.
Go Debugging
I then decided I need to start using a debugger that supports Go. I discovered another course by Michael Van Sickle, Debugging Go Applications with Delve (link). Extremely good “must watch” course. I finished sections 1, 2 & 6. Practiced debugging in both the command line and with Visual Studio Code. I have extensive experience with Visual Studio Professional in C/C++ and using Go with Visual Studio Code feels very natural.
Now I will try to figure out remote debugging with Visual Studio Code. My goal is developing on Windows and debugging both Linux and Windows Go programs on a remote system which is my laptop running Windows 10 Home and WSL Debian.
Delve Debugging Commands to study:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
args ------------------------ Print function arguments. break (alias: b) ------------ Sets a breakpoint. breakpoints (alias: bp) ----- Print out info for active breakpoints. call ------------------------ Resumes process, injecting a function call (EXPERIMENTAL!!!) clear ----------------------- Deletes breakpoint. clearall -------------------- Deletes multiple breakpoints. condition (alias: cond) ----- Set breakpoint condition. config ---------------------- Changes configuration parameters. continue (alias: c) --------- Run until breakpoint or program termination. deferred -------------------- Executes command in the context of a deferred call. disassemble (alias: disass) - Disassembler. down ------------------------ Move the current frame down. edit (alias: ed) ------------ Open where you are in $DELVE_EDITOR or $EDITOR exit (alias: quit | q) ------ Exit the debugger. frame ----------------------- Set the current frame, or execute command on a different frame. funcs ----------------------- Print list of functions. goroutine (alias: gr) ------- Shows or changes current goroutine goroutines (alias: grs) ----- List program goroutines. help (alias: h) ------------- Prints the help message. libraries ------------------- List loaded dynamic libraries list (alias: ls | l) -------- Show source code. locals ---------------------- Print local variables. next (alias: n) ------------- Step over to next source line. on -------------------------- Executes a command when a breakpoint is hit. print (alias: p) ------------ Evaluate an expression. regs ------------------------ Print contents of CPU registers. restart (alias: r) ---------- Restart process. set ------------------------- Changes the value of a variable. source ---------------------- Executes a file containing a list of delve commands sources --------------------- Print list of source files. stack (alias: bt) ----------- Print stack trace. step (alias: s) ------------- Single step through program. step-instruction (alias: si) Single step a single cpu instruction. stepout --------------------- Step out of the current function. thread (alias: tr) ---------- Switch to the specified thread. threads --------------------- Print out info for every traced thread. trace (alias: t) ------------ Set tracepoint. types ----------------------- Print list of types up -------------------------- Move the current frame up. vars ------------------------ Print package variables. whatis ---------------------- Prints type of an expression. |
Day #23 – July 3, 2019
In practicing the Iota and Constant expression section of “Go: Getting Started Section 4 – Working with Primitive Data Types”, I ran into a surprise. Take a look at the following code and try to guess what will be printed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package main import ( "fmt" ) const ( first = iota second = iota third = 0 fourth = iota fifth ) func main() { fmt.Println(first, second, third, fourth, fifth) } |
Output:
1 |
0 1 0 3 4 |
I set third
to zero and started another iota
. I expected fourth
to be zero, thinking that constant iota expression started over. Instead, it is three. If I move fourth
and fifth
to another constant block, it works as I expected.
Day #24 – July 4, 2019
Today, I ran across a #golang tweet with an interesting quiz. One that I did not know the answer to. So I sent out a tweet asking for the answer. This tweet surprised me and generated a large amount of activity (4,000+).
The original tweet by @dastanng:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#golang what does this program print? package main import ( "fmt" ) type foo struct {} func (f *foo) hi() { fmt.Println("Hi.") } func main() { var f *foo f.hi() } |
I received an answer from Bill Kennedy @goinggodotnet : “Methods are just functions and think of nil as a value like any other that the variable could contain. So nil is passed into the method call as the receiver value. Since the method does nothing with the receiver variable, there are no exceptions.”
I then spent an hour or so studying methods with information from this article: “Golang Methods Tutorial with Examples”
Day #25 – July 5, 2019
This morning I started another course by Michael Van Sickle: “Building Better Go Web Apps with the Gorilla Toolkit“. This time I had an objective. I wanted to study HTTP/web sessions and cookies in Go. I started with Part 6: “Gorilla/Securecookie: Securing Sensitive Information” and then part 8 “Gorilla/Sessions: Persisting Information between Calls”. Excellent material.
Day #26 – July 6, 2019
Today, no Go studies. I published two articles today: Google Cloud – HTTP Load Balancer File Upload Error and Google Cloud Run – Minimizing Cold Starts.
Day #27 – 30 July 7 – 10, 2019
I was not able to study at all. Too many commitments. One of them was a presentation on Google Cloud Run.
Day #31 – July 11, 2019
This is the planned day for taking the Pluralsight “Go Skill Assessment” to measure what I accomplished.
July 14, 2019
Go to Part 2 to see my results.
Credits
I write free articles about technology. Recently, I learned about Pexels.com which provides free images. The image in this article is courtesy of Vural Yavas at Pexels.
I design software for enterprise-class systems and data centers. My background is 30+ years in storage (SCSI, FC, iSCSI, disk arrays, imaging) virtualization. 20+ years in identity, security, and forensics.
For the past 14+ years, I have been working in the cloud (AWS, Azure, Google, Alibaba, IBM, Oracle) designing hybrid and multi-cloud software solutions. I am an MVP/GDE with several.
1 Pingback