mirror of
https://github.com/MichaelCade/90DaysOfDevOps.git
synced 2025-07-31 07:10:52 +07:00
Add 2023 day 84 What is an API
Signed-off-by: Alistair Hey <alistair@heyal.co.uk>
This commit is contained in:
17
2023/day2-ops-code/synchronous/generator/Dockerfile
Normal file
17
2023/day2-ops-code/synchronous/generator/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# Set the base image to use
|
||||
FROM golang:1.17-alpine
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the source code into the container
|
||||
COPY . .
|
||||
|
||||
# Build the Go application
|
||||
RUN go build -o main .
|
||||
|
||||
# Expose the port that the application will run on
|
||||
EXPOSE 8080
|
||||
|
||||
# Define the command that will run when the container starts
|
||||
CMD ["/app/main"]
|
5
2023/day2-ops-code/synchronous/generator/go.mod
Normal file
5
2023/day2-ops-code/synchronous/generator/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module main
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/go-sql-driver/mysql v1.7.0
|
2
2023/day2-ops-code/synchronous/generator/go.sum
Normal file
2
2023/day2-ops-code/synchronous/generator/go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
139
2023/day2-ops-code/synchronous/generator/main.go
Normal file
139
2023/day2-ops-code/synchronous/generator/main.go
Normal file
@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func generateAndStoreString() (string, error) {
|
||||
// Connect to the database
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Generate a random string
|
||||
// Define a string of characters to use
|
||||
characters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// Generate a random string of length 10
|
||||
randomString := make([]byte, 64)
|
||||
for i := range randomString {
|
||||
randomString[i] = characters[rand.Intn(len(characters))]
|
||||
}
|
||||
|
||||
// Insert the random number into the database
|
||||
_, err = db.Exec("INSERT INTO generator(random_string) VALUES(?)", string(randomString))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Printf("Random string %s has been inserted into the database\n", string(randomString))
|
||||
return string(randomString), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create a new HTTP server
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
}
|
||||
|
||||
err := createGeneratordb()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
quit := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
checkStringReceived()
|
||||
case <-quit:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Handle requests to /generate
|
||||
http.HandleFunc("/new", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Generate a random number
|
||||
randomString, err := generateAndStoreString()
|
||||
if err != nil {
|
||||
http.Error(w, "unable to generate and save random string", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
print(fmt.Sprintf("random string: %s", randomString))
|
||||
w.Write([]byte(randomString))
|
||||
})
|
||||
|
||||
// Start the server
|
||||
fmt.Println("Listening on port 8080")
|
||||
err = server.ListenAndServe()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func createGeneratordb() error {
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// try to create a table for us
|
||||
_, err = db.Exec("CREATE TABLE IF NOT EXISTS generator(random_string VARCHAR(100), seen BOOLEAN)")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func checkStringReceived() {
|
||||
// get a list of strings from database that dont have the "seen" bool set top true
|
||||
// loop over them and make a call to the requestor's 'check' endpoint and if we get a 200 then set seen to true
|
||||
|
||||
// Connect to the database
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
if err != nil {
|
||||
print(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Insert the random number into the database
|
||||
results, err := db.Query("SELECT random_string FROM generator WHERE seen IS NOT true")
|
||||
if err != nil {
|
||||
print(err)
|
||||
}
|
||||
|
||||
// loop over results
|
||||
for results.Next() {
|
||||
var randomString string
|
||||
err = results.Scan(&randomString)
|
||||
if err != nil {
|
||||
print(err)
|
||||
}
|
||||
|
||||
// make a call to the requestor's 'check' endpoint
|
||||
// if we get a 200 then set seen to true
|
||||
r, err := http.Get("http://requestor-service:8080/check/" + randomString)
|
||||
if err != nil {
|
||||
print(err)
|
||||
}
|
||||
if r.StatusCode == 200 {
|
||||
_, err = db.Exec("UPDATE generator SET seen = true WHERE random_string = ?", randomString)
|
||||
if err != nil {
|
||||
print(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(fmt.Sprintf("Random string has not been received by the requestor: %s", randomString))
|
||||
}
|
||||
}
|
||||
}
|
69
2023/day2-ops-code/synchronous/k8s.yaml
Normal file
69
2023/day2-ops-code/synchronous/k8s.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: requestor
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: requestor
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: requestor
|
||||
spec:
|
||||
containers:
|
||||
- name: requestor
|
||||
image: heyal/requestor:sync
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: requestor-service
|
||||
spec:
|
||||
selector:
|
||||
app: requestor
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: generator
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: generator
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: generator
|
||||
spec:
|
||||
containers:
|
||||
- name: generator
|
||||
image: heyal/generator:sync
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: generator-service
|
||||
spec:
|
||||
selector:
|
||||
app: generator
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
17
2023/day2-ops-code/synchronous/requestor/Dockerfile
Normal file
17
2023/day2-ops-code/synchronous/requestor/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# Set the base image to use
|
||||
FROM golang:1.17-alpine
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the source code into the container
|
||||
COPY . .
|
||||
|
||||
# Build the Go application
|
||||
RUN go build -o main .
|
||||
|
||||
# Expose the port that the application will run on
|
||||
EXPOSE 8080
|
||||
|
||||
# Define the command that will run when the container starts
|
||||
CMD ["/app/main"]
|
5
2023/day2-ops-code/synchronous/requestor/go.mod
Normal file
5
2023/day2-ops-code/synchronous/requestor/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module main
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/go-sql-driver/mysql v1.7.0
|
2
2023/day2-ops-code/synchronous/requestor/go.sum
Normal file
2
2023/day2-ops-code/synchronous/requestor/go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
134
2023/day2-ops-code/synchronous/requestor/main.go
Normal file
134
2023/day2-ops-code/synchronous/requestor/main.go
Normal file
@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func storeString(input string) error {
|
||||
// Connect to the database
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
defer db.Close()
|
||||
// Insert the random number into the database
|
||||
_, err = db.Exec("INSERT INTO requestor(random_string) VALUES(?)", input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Random string %s has been inserted into the database\n", input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStringFromDB(input string) error {
|
||||
// see if the string exists in the db, if so return nil
|
||||
// if not, return an error
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
defer db.Close()
|
||||
result, err := db.Query("SELECT * FROM requestor WHERE random_string = ?", input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
var randomString string
|
||||
err = result.Scan(&randomString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if randomString == input {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("string not found")
|
||||
}
|
||||
|
||||
func getStringFromGenerator() {
|
||||
// make a request to the generator
|
||||
// save sthe string to db
|
||||
r, err := http.Get("http://generator-service:8080/new")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(fmt.Sprintf("body from generator: %s", string(body)))
|
||||
|
||||
storeString(string(body))
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
// setup a goroutine loop calling the generator every minute, saving the result in the DB
|
||||
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
quit := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
getStringFromGenerator()
|
||||
case <-quit:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a new HTTP server
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
}
|
||||
|
||||
err := createRequestordb()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
// Handle requests to /generate
|
||||
http.HandleFunc("/check/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// get the value after check from the url
|
||||
id := strings.TrimPrefix(r.URL.Path, "/check/")
|
||||
|
||||
// check if it exists in the db
|
||||
err := getStringFromDB(id)
|
||||
if err != nil {
|
||||
http.Error(w, "string not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "string found", http.StatusOK)
|
||||
})
|
||||
|
||||
// Start the server
|
||||
fmt.Println("Listening on port 8080")
|
||||
err = server.ListenAndServe()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func createRequestordb() error {
|
||||
db, err := sql.Open("mysql", "root:password@tcp(mysql:3306)/mysql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// try to create a table for us
|
||||
_, err = db.Exec("CREATE TABLE IF NOT EXISTS requestor(random_string VARCHAR(100))")
|
||||
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user