From 776e42da90fee0d0cb511cbf44b200b5ececeee8 Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Fri, 25 Dec 2020 07:28:53 +1100 Subject: [PATCH] go intro --- golang/introduction/app/app.go | 36 +++ golang/introduction/app/customers.go | 28 ++ golang/introduction/app/go.mod | 3 + golang/introduction/dockerfile | 13 + golang/introduction/readme.md | 429 +++++++++++++++++++++++++++ 5 files changed, 509 insertions(+) create mode 100644 golang/introduction/app/app.go create mode 100644 golang/introduction/app/customers.go create mode 100644 golang/introduction/app/go.mod create mode 100644 golang/introduction/dockerfile create mode 100644 golang/introduction/readme.md diff --git a/golang/introduction/app/app.go b/golang/introduction/app/app.go new file mode 100644 index 0000000..e0d9d0a --- /dev/null +++ b/golang/introduction/app/app.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" +) + +func main() { + + customers := GetCustomers() + + for _, customer := range customers { + //we can access the "customer" variable in this approach + fmt.Println(customer) +} + +} + +func getData() (customers []string) { + + customers = []string{ "Marcel Dempers", "Bob Smith", "John Smith"} + + customers = append(customers, "Ben Spain") + customers = append(customers, "Aleem Janmohamed") + customers = append(customers, "Jamie le Notre") + customers = append(customers, "Victor Savkov") + customers = append(customers, "P The Admin") + customers = append(customers, "Adrian Oprea") + customers = append(customers, "Jonathan D") + + for _, customer := range customers { + + fmt.Println(customer) + } + + return customers +} \ No newline at end of file diff --git a/golang/introduction/app/customers.go b/golang/introduction/app/customers.go new file mode 100644 index 0000000..2865711 --- /dev/null +++ b/golang/introduction/app/customers.go @@ -0,0 +1,28 @@ +package main + +type ( + Customer struct { + FirstName string + LastName string + FullName string + } +) + +func GetCustomers()(customers []Customer) { + + marcel := Customer{ FirstName: "Marcel", LastName: "Dempers"} + + customers = append(customers, marcel, + Customer{ FirstName: "Ben", LastName: "Spain" }, + Customer{ FirstName: "Aleem", LastName: "Janmohamed" }, + Customer{ FirstName: "Jamie", LastName: "le Notre" }, + Customer{ FirstName: "Victor", LastName: "Savkov" }, + Customer{ FirstName: "P", LastName: "The Admin" }, + Customer{ FirstName: "Adrian", LastName: "Oprea" }, + Customer{ FirstName: "Jonathan", LastName: "D" }, + + ) + + return customers + +} \ No newline at end of file diff --git a/golang/introduction/app/go.mod b/golang/introduction/app/go.mod new file mode 100644 index 0000000..853db6b --- /dev/null +++ b/golang/introduction/app/go.mod @@ -0,0 +1,3 @@ +module github.com/docker-development-youtube-series/golang/introdouction/app + +go 1.15 diff --git a/golang/introduction/dockerfile b/golang/introduction/dockerfile new file mode 100644 index 0000000..539283d --- /dev/null +++ b/golang/introduction/dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.15 as dev + +WORKDIR /work + +FROM golang:1.15 as build + +WORKDIR /app +COPY ./app/* /app/ +RUN go build -o app + +FROM alpine as runtime +COPY --from=build /app/app / +CMD ./app \ No newline at end of file diff --git a/golang/introduction/readme.md b/golang/introduction/readme.md new file mode 100644 index 0000000..3261b4a --- /dev/null +++ b/golang/introduction/readme.md @@ -0,0 +1,429 @@ +# Introduction to Learning Go + +Go can be downloaded from [golang.org](https://golang.org/doc/install)
+ +Test your `go` installation: + +``` +go version +``` + +# Run Go in Docker + +We can also run go in a small docker container:
+ +``` +cd golang\introduction + +docker build --target dev . -t go +docker run -it -v ${PWD}:/work go sh +go version + +``` + +# Code Structure + +https://golang.org/doc/code.html + +* Package: + - Source files in same directory that are compiled together + - Have visibility on all source files in the same package + +* Modules: + - Collection of packages that are released together + +Our repository can contain one or more go modules, but usually 1. + - At the root of the repo + +`go.mod` Declares module path + import path for packages. (Where to download them) + - When we write our own program, we can define a module path + - This allows us to publish our code (if we want), so others can download it + - The module path could be something like `github.com/google/go-cmp` + - Makes it easy for other programs to consume our module + +# Our first Program + +* Create a folder containing our application + +``` +mkdir app + +``` + +* Define a module path (github.com/docker-development-youtube-series/golang/introdouction/app) + +``` +# change directory to your application source code + +cd app + +# create a go module file + +go mod init github.com/docker-development-youtube-series/golang/introdouction/app + +``` + +* Create basic source code + +In the `app` folder, create a program called `app.go` +Paste the following content into it. + +``` +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world.") +} +``` + +* Run your application code + +You can run your application + +``` +go run app.go +``` + +# Building our Program + +Build your application into a static binary:
+ +``` +go build +``` + +This will produce a compiled program called `app` +You can run this program easily: + +``` +./app +``` + +# Install your application (optional) + +"This command builds the app command, producing an executable binary.
+It then installs that binary as $HOME/go/bin/app (or, under Windows, %USERPROFILE%\go\bin\app.exe)" + +``` +go install github.com/docker-development-youtube-series/golang/introdouction/app +``` + +# The Code + +## Functions + +In the video we cover writing functions.
+It allows us to execute a block of code
+You want to give your function a single purpose
+Functions can have an input and return an output
+Well thought out functions makes it easier to write tests
+ +Instead of doing a boring `x + y` function that adds two numbers, let's do something a little +more realistic but still basic: + +``` +// This function returns some data +// The data could be coming from a database, or a file. +// The person calling the function should not care +// Since the function does not leak its Data provider + +func getData(inputs)(outputs){ +} + +// functions can take multiple inputs, and return multiple outputs + +// lets say we have 1) customers and 2) the cities they are from +// we may want to 1) get a list of customers and 2) get a list of cities +// therefore we have 2 types of data, 1) customers 2) cities +// let's improve our function so its gets data based on the type + +func getData(customerId int) (customer string) { +} +``` + +## Variables + +To hold data in programming languages, we use variables.
+Variables take up space in memory, so we want to keep it minimal.
+Let's declare variables in our function + +``` +func getData(customerId int) (customer string) { + var firstName = "Marcel" + lastName := "Dempers" + + fullName := firstName + " " + lastName + return fullName + + //or we can return the computation instead of adding another variable! + return firstName + " " + lastName + + //or we dont even need to declare variables :) + return "Marcel Dempers" + +} + +``` + +## Control Flows (if\else) + +You can see we're not using the `customerId` input in our function.
+Let's use it!
+ +Control flows allow us to add "rules" to our code.
+"If this is the case, then do that, else do something else". + +So let's say we have a customer ID 1 coming in, we may only want to +return our customer if it matches the `customerId` + +``` + +func getData(customerId int) (customer string) { + + if customerId == 1 { + return "Marcel Dempers" + } else if customerId == 2 { + return "Bob Smith" + } else { + return "" + } + +} + + +``` + +Let's invoke our function : + +``` +//in the main() function + +//get our customer +customer := getData(1) +fmt.Println(customer) + +//get the wrong customer +customer := getData(3) +fmt.Println(customer) + + +``` +## Arrays + +At the moment, we can only return 1 customer at a time on our function.
+Realisticly we need the ability to return more data, not just a single customer.
+ +Arrays allow us to make a collection of variables of the same type.
+We can now return a list of customers!
+ +Let's change our function to get an array of customers! + +``` +func getData() (customers [2]string) { + //create 1 record + customer := "Marcel Dempers" + + //assign our customer to the array + customers[0] = customer + + //OR we can assign it like this + customers[1] = "Bob Smith" + + //send it back to the caller + return customers + +} + +``` + +Now we also have to change our calling function to expect an array: + +``` +customers := getData() + +fmt.Println(customers) + +``` + +## Slices + +Since arrays are fixed size, Slices are a dynamically-sized view into arrays. +Let's create a slice instead of array so we can add customers dynamically! + +``` +func getData() (customers []string) { + + //initialise our slice of type string + customers = []string{ "Marcel Dempers", "Bob Smith", "John Smith"} + + //add more legendary customers dynamically + customers = append(customers, "Ben Spain") + customers = append(customers, "Aleem Janmohamed") + customers = append(customers, "Jamie le Notre") + customers = append(customers, "Victor Savkov") + customers = append(customers, "P The Admin") + customers = append(customers, "Adrian Oprea") + customers = append(customers, "Jonathan D") + + //send it back to the caller + return customers + +} + + +``` + +## Loops + +Loops are used to iterate over collections, lists, arrays etc.
+Let's say we need to loop through our customers + +In the `main()` function, we can grab the list of customers and loop them. +In this demo, we'll cover a basic for loop, but there are several approaches to writing loops. + +``` +//loop forever +for { + //any code in here will run forever! + + fmt.Println("Infinite Loop 1") + time.Sleep(time.Second) + + //unless we break out the loop like this + break +} + +//loop for x number of loops +for x := 0; x < 10; x++ { + + //any code in here will run 10 times! (unless we break!) + fmt.Println(customers[x]) + +} + +//loop for ALL our customer + +for x, customer := range customers { + + //we can access the "customer" variable in this approach + customer = customers[x] + fmt.Println(customer) + + //OR + //we can use the supplied customer from the loop + // and silence the x variable, replace it with a _ character + fmt.Println(customer) +} + +``` + +## Structs + +So far so good, however, customer data is not useful as strings.
+Customers can have a firstname, lastname, and more properties.
+ +For this purpose we'd like to group some variables into a single variable.
+This is what `struct` allows us to do.
+Let's create a `struct` for our customer + +Let's create a new `go` file called `customers.go` + +``` +package main + +Customer struct { + FirstName string + LastName string + FullName string +} + +``` + +Let's put it all together: + +In `customers.go`, let's create a function to get customers + +``` +func GetCustomers()(customers []Customer) { + + //we can declare customers like this: + marcel := Customer{ FirstName: "Marcel", LastName: "Dempers" } + + customers = append(customers, + Customer{ FirstName: "Marcel", LastName: "Dempers" }, + Customer{ FirstName: "Ben", LastName: "Spain" }, + Customer{ FirstName: "Aleem", LastName: "Janmohamed" }, + Customer{ FirstName: "Jamie", LastName: "le Notre" }, + Customer{ FirstName: "Victor", LastName: "Savkov" }, + Customer{ FirstName: "P", LastName: "The Admin" }, + Customer{ FirstName: "Adrian", LastName: "Oprea" }, + Customer{ FirstName: "Jonathan", LastName: "D" }, + ) + + return customers + +} +``` + +In `main()` we can now call our shiny new function + + +``` +customers := GetCustomers() + +for _, customer := range customers { + //we can access the "customer" variable in this approach + fmt.Println(customer) +} +``` + +# Docker + +For our dev environment, we have a simple image using `go`
+We also set a work directory and alias the target as `dev` + +This means we can use this container layer as a development environment.
+Later down the track we can add debuggers in here for example.
+Checkout my debugging video for go: https://youtu.be/kToyI16IFxs
+ + +## Development environment + +``` +FROM golang:1.15 as dev + +WORKDIR /work +``` + +## Building our code + +``` +FROM golang:1.15 as build + +WORKDIR /app +COPY ./app/* /app/ +RUN go build -o app +``` + +## The Runtime + +``` +FROM alpine as runtime +COPY --from=build /app/app / +CMD ./app +``` + +## Building the Container + +``` +docker build . -t customer-app + +``` + +## Running the Container + +``` +docker run customer-app +```