Heim  >  Artikel  >  Backend-Entwicklung  >  Erstellen einer API mit Go, PostgreSQL, Google Cloud und CockroachDB

Erstellen einer API mit Go, PostgreSQL, Google Cloud und CockroachDB

Linda Hamilton
Linda HamiltonOriginal
2024-10-24 07:02:30758Durchsuche

Ich habe eine API mit Go und PostgreSQL erstellt, eine CI/CD-Pipeline mit Google Cloud Run, Cloud Build, Secret Manager und Artifact Registry eingerichtet und die Cloud Run-Instanz mit CockroachDB verbunden.

Die API basiert auf dem Spiel Crisis Core: Final Fantasy VII, um „Materia Fusion“ zu simulieren. Die Zielgruppe dieses Artikels sind Entwickler, die lediglich wissen möchten, wie die API erstellt und bereitgestellt wird. Ich habe einen weiteren Artikel, in dem ich über alles spreche, was ich bei der Arbeit an diesem Projekt gelernt habe, was nicht funktioniert hat und über das Verständnis und die Übersetzung der Materia-Fusion-Regeln des Spiels (Link folgt in Kürze).

Links zur einfachen Referenz

  • GitHub-Repo und README
  • Swagger (OpenAPI) Dokumentation und Tests
  • Öffentliche Briefträgersammlung
  • Quelle des Domänenmodells

API-Ziel

3 Endpunkte – Gesundheitsprüfung (GET), Liste aller Materia (GET) und Simulation der Materia-Fusion (POST)

Das Domänenmodell

Materia (sowohl Singular als auch Plural) ist eine Kristallkugel, die als Quelle der Magie dient. Es gibt 144 verschiedene Materia im Spiel, die grob in vier Kategorien eingeteilt werden: „Magie“, „Befehl“, „Unterstützung“ und „Unabhängig“. Um jedoch die Regeln der Materia-Fusion herauszufinden, war es einfacher, 32 interne Kategorien basierend auf ihrem Fusionsverhalten und 8 Grade innerhalb dieser Kategorien zu haben (siehe Referenz). .

Eine Materia wird „gemeistert“, wenn sie für eine bestimmte Dauer verwendet wird. Die Dauer ist hier nicht wichtig.

Am wichtigsten ist, dass zwei Materia zu einer neuen Materia verschmolzen werden können. Die Fusionsregeln werden beeinflusst durch:

  • Ob eine oder beide Materia beherrscht werden.
  • Welche Materia kommt zuerst (wie in X ist Y nicht unbedingt gleich Y X).
  • Materia interne Kategorie.
  • Materia-Grad.

Building an API with Go, PostgreSQL, Google Cloud and CockroachDB

Und es gibt VIELE Ausnahmen, wobei einige Regeln drei Ebenen verschachtelter If-Else-Logik haben. Dadurch entfällt die Möglichkeit, eine einfache Tabelle in der Datenbank zu erstellen und darin 1000 Regeln zu speichern oder sich eine Formel auszudenken, um sie alle zu beherrschen.

Kurz gesagt, wir brauchen:

  1. Eine Tabelle materia mit den Spalten name(string), materia_type(ENUM) (die 32 internen Kategorien), grade(integer), display_materia_type(ENUM) (die 4 im Spiel verwendeten Kategorien), description(string) und id( Ganzzahl) als automatisch inkrementierender Primärschlüssel.
  2. Eine Datenstruktur zur Kapselung des Grundregelformats MateriaTypeA MateriaTypeB = MateriaTypeC.
  3. Code zur Verwendung der grundlegenden und komplexen Regeln zur Bestimmung der Ausgabemateria im Hinblick auf ihre interne Kategorie und Note.

1. Einrichten der lokalen PostgreSQL-Datenbank

Idealerweise können Sie die Datenbank von der Website selbst installieren. Aber das pgAdmin-Tool konnte aus irgendeinem Grund keine Verbindung zur Datenbank herstellen, also habe ich Homebrew verwendet.

Installation

brew install postgresql@17

Dadurch wird eine ganze Reihe von CLI-Binärdateien installiert, um die Verwendung der Datenbank zu erleichtern.

Optional: /opt/homebrew/opt/postgresql@17/bin zur Variablen $PATH hinzufügen.

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb

Erstellen Sie den Benutzer und die Berechtigungen

-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;

Erstellen Sie die Tabelle

CREATE TYPE display_materia_type AS ENUM ('Magic', 'Command', 'Support', 'Independent');

CREATE TYPE materia_type AS ENUM ('Fire', 'Ice', 'Lightning', 'Restore', 'Full Cure', 'Status Defense', 'Defense', 'Absorb Magic', 'Status Magic', 'Fire & Status', 'Ice & Status', 'Lightning & Status', 'Gravity', 'Ultimate', 'Quick Attack', 'Quick Attack & Status', 'Blade Arts', 'Blade Arts & Status', 'Fire Blade', 'Ice Blade', 'Lightning Blade', 'Absorb Blade', 'Item', 'Punch', 'SP Turbo', 'HP Up', 'AP Up', 'ATK Up', 'VIT Up', 'MAG Up', 'SPR Up', 'Dash', 'Dualcast', 'DMW', 'Libra', 'MP Up', 'Anything');

CREATE TABLE materia (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    materia_type materia_type NOT NULL,
    grade integer NOT NULL,
    display_materia_type display_materia_type,
    description text
    CONSTRAINT materia_pkey PRIMARY KEY (id)
);

-- The primary key 'id' should auto-increment by 1 for every row entry.
CREATE SEQUENCE materia_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE materia_id_seq OWNED BY materia.id;

ALTER TABLE ONLY materia ALTER COLUMN id SET DEFAULT nextval('materia_id_seq'::REGCLASS);

Fügen Sie die Daten hinzu

Erstellen Sie eine Excel-Tabelle mit Tabellenkopf und Daten und exportieren Sie sie als CSV-Datei. Führen Sie dann den Befehl aus:

COPY materia(name,materia_type,grade,display_materia_type,description) FROM
 '<path_to_csv_file>/materiadata.csv' DELIMITER ',' CSV HEADER;

2. Erstellen des Go-Servers

Erstellen Sie den Boilerplate-Code mit autostrada.dev. Fügen Sie die Optionen API, Postgresql, httprouter, env var config, tinted logging, git, live reload und makefile hinzu. Am Ende erhalten wir eine Dateistruktur wie diese:

? codebase
├─ cmd
│  └─ api
│     ├─ errors.go
│     ├─ handlers.go
│     ├─ helpers.go
│     ├─ main.go
│     ├─ middleware.go
│     └─ server.go
├─ internal
│  ├─ database --- db.go
│  ├─ env --- env.go
│  ├─ request --- json.go
│  ├─ response --- json.go
│  └─ validator
│     ├─ helpers.go
│     └─ validators.go
├─ go.mod
├─ LICENSE
├─ Makefile
├─ README.md
└─ README.html

.env-Datei

Der Boilerplate-Generator hat Code erstellt, um Umgebungsvariablen abzurufen und sie dem Code hinzuzufügen, aber wir können es einfacher machen, die Werte zu verfolgen und zu aktualisieren.

Erstellen Sie die Datei /.env. Fügen Sie die folgenden Werte hinzu:

HTTP_PORT=4444
DB_DSN=go_client:<password>@localhost:5432/materiafusiondb?sslmode=disable
API_TIMEOUT_SECONDS=5
API_CALLS_ALLOWED_PER_SECOND=1

Fügen Sie die Godotenv-Bibliothek hinzu:

go get github.com/joho/godotenv

Fügen Sie Folgendes zu main.go hinzu:

// At the beginning of main():
err := godotenv.Load(".env") // Loads environment variables from .env file
if err != nil { // This will be true in prod, but that's fine.
  fmt.Println("Error loading .env file")
}


// Modify config struct:
type config struct {
  baseURL string
  db      struct {
    dsn string
  }
  httpPort                 int
  apiTimeout               int
  apiCallsAllowedPerSecond float64
}

// Modify run() to use the new values from .env:
cfg.httpPort = env.GetInt("HTTP_PORT")
cfg.db.dsn = env.GetString("DB_DSN")
cfg.apiTimeout = env.GetInt("API_TIMEOUT_SECONDS")
cfg.apiCallsAllowedPerSecond = float64(env.GetInt("API_CALLS_ALLOWED_PER_SECOND"))

// cfg.baseURL = env.GetString("BASE_URL") - not required

Middleware und Routen

Das Boilerplate verfügt bereits über eine Middleware zur Wiederherstellung nach Paniken. Wir werden drei weitere hinzufügen: Inhaltstypprüfung, Ratenbegrenzung und API-Timeout-Schutz.

Mautstellenbibliothek hinzufügen:

go get github.com/didip/tollbooth

Aktualisieren Sie

func (app *application) contentTypeCheck(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.Header.Get("Content-Type") != "application/json" {
   app.unsupportedMediaType(w, r)

   return
  }
  next.ServeHTTP(w, r)
 })
}


func (app *application) rateLimiter(next http.Handler) http.Handler {
 limiter := tollbooth.NewLimiter(app.config.apiCallsAllowedPerSecond, nil)
 limiter.SetIPLookups([]string{"X-Real-IP", "X-Forwarded-For", "RemoteAddr"})

 return tollbooth.LimitHandler(limiter, next)
}


func (app *application) apiTimeout(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  timeoutDuration := time.Duration(app.config.apiTimeout) * time.Second

  ctx, cancel := context.WithTimeout(r.Context(), timeoutDuration)
  defer cancel()

  r = r.WithContext(ctx)

  done := make(chan struct{})

  go func() {
   next.ServeHTTP(w, r)
   close(done)
  }()

  select {
  case <-done:
   return
  case <-ctx.Done():
   app.gatewayTimeout(w, r)
   return
  }
 })
}

Die Middleware muss zu den Routen hinzugefügt werden. Sie können entweder zu allen Routen oder zu bestimmten Routen hinzugefügt werden. In unserem Fall ist die Überprüfung des Inhaltstyps (d. h. die Vorgabe, dass die Eingabeheader den Inhaltstyp: application/json enthalten) nur für POST-Anfragen erforderlich. Ändern Sie also „routes.go“ wie folgt:

func (app *application) routes() http.Handler {
 mux := httprouter.New()

 mux.NotFound = http.HandlerFunc(app.notFound)
 mux.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowed)

 // Serve the Swagger UI. Uncomment this line later
 // mux.Handler("GET", "/docs/*any", httpSwagger.WrapHandler)

 mux.HandlerFunc("GET", "/status", app.status)
 mux.HandlerFunc("GET", "/materia", app.getAllMateria)

 // Adding content-type check middleware to only the POST method
 mux.Handler("POST", "/fusion", app.contentTypeCheck(http.HandlerFunc(app.fuseMateria)))

 return app.chainMiddlewares(mux)
}

func (app *application) chainMiddlewares(next http.Handler) http.Handler {
 middlewares := []func(http.Handler) http.Handler{
  app.recoverPanic,
  app.apiTimeout,
  app.rateLimiter,
 }

 for _, middleware := range middlewares {
  next = middleware(next)
 }

 return next
}

Fehlerbehandlung

Fügen Sie die folgenden Methoden zu /api/errors.go hinzu, um die Middleware-Funktionen zu unterstützen:

func (app *application) unsupportedMediaType(w http.ResponseWriter, r *http.Request) {
 message := fmt.Sprintf("The %s Content-Type is not supported", r.Header.Get("Content-Type"))
 app.errorMessage(w, r, http.StatusUnsupportedMediaType, message, nil)
}

func (app *application) gatewayTimeout(w http.ResponseWriter, r *http.Request) {
 message := "Request timed out"
 app.errorMessage(w, r, http.StatusGatewayTimeout, message, nil)
}

Anforderungs- und Antwortstrukturdateien

/api/dtos.go :

package main

// MateriaDTO provides Materia details - Name, Description and Type (Magic / Command / Support / Independent)
type MateriaDTO struct {
 Name        string `json:"name" example:"Thunder"`
 Type        string `json:"type" example:"Magic"`
 Description string `json:"description" example:"Shoots lightning forward dealing thunder damage."`
}

// StatusDTO provides status of the server
type StatusDTO struct {
 Status string `json:"Status" example:"OK"`
}

// ErrorResponseDTO provides Error message
type ErrorResponseDTO struct {
 Error string `json:"Error" example:"The server encountered a problem and could not process your request"`
}

/api/requests.go :

brew install postgresql@17

Validator aus dem generierten Code wird später verwendet, um die Eingabefelder für den Fusionsendpunkt zu validieren.

Datenstruktur für die Kombinationsregeln

Erstellen Sie die Datei /internal/crisis-core-materia-fusion/constants.go

Fügen Sie Folgendes hinzu:

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb

Eine vollständige Liste der 32 MateriaTypes finden Sie hier.

Erstellen Sie die Datei /internal/crisis-core-materia-fusion/models.go

Fügen Sie Folgendes hinzu:

-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;

Die vollständige Liste der Regeln finden Sie hier.

Handler für Materia in api/handlers.go

CREATE TYPE display_materia_type AS ENUM ('Magic', 'Command', 'Support', 'Independent');

CREATE TYPE materia_type AS ENUM ('Fire', 'Ice', 'Lightning', 'Restore', 'Full Cure', 'Status Defense', 'Defense', 'Absorb Magic', 'Status Magic', 'Fire & Status', 'Ice & Status', 'Lightning & Status', 'Gravity', 'Ultimate', 'Quick Attack', 'Quick Attack & Status', 'Blade Arts', 'Blade Arts & Status', 'Fire Blade', 'Ice Blade', 'Lightning Blade', 'Absorb Blade', 'Item', 'Punch', 'SP Turbo', 'HP Up', 'AP Up', 'ATK Up', 'VIT Up', 'MAG Up', 'SPR Up', 'Dash', 'Dualcast', 'DMW', 'Libra', 'MP Up', 'Anything');

CREATE TABLE materia (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    materia_type materia_type NOT NULL,
    grade integer NOT NULL,
    display_materia_type display_materia_type,
    description text
    CONSTRAINT materia_pkey PRIMARY KEY (id)
);

-- The primary key 'id' should auto-increment by 1 for every row entry.
CREATE SEQUENCE materia_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE materia_id_seq OWNED BY materia.id;

ALTER TABLE ONLY materia ALTER COLUMN id SET DEFAULT nextval('materia_id_seq'::REGCLASS);

Serverinterner Cache

Wir verwenden einen serverinternen Cache, weil:

  1. Die aus der Datenbank abgerufenen Daten ändern sich nie.
  2. Die gleichen Daten werden sowohl von Materia- als auch von Fusionsendpunkten verwendet.

main.go aktualisieren:

COPY materia(name,materia_type,grade,display_materia_type,description) FROM
 '<path_to_csv_file>/materiadata.csv' DELIMITER ',' CSV HEADER;

API/helpers.go aktualisieren:

? codebase
├─ cmd
│  └─ api
│     ├─ errors.go
│     ├─ handlers.go
│     ├─ helpers.go
│     ├─ main.go
│     ├─ middleware.go
│     └─ server.go
├─ internal
│  ├─ database --- db.go
│  ├─ env --- env.go
│  ├─ request --- json.go
│  ├─ response --- json.go
│  └─ validator
│     ├─ helpers.go
│     └─ validators.go
├─ go.mod
├─ LICENSE
├─ Makefile
├─ README.md
└─ README.html

Handler für die Fusion in api/handlers.go

HTTP_PORT=4444
DB_DSN=go_client:<password>@localhost:5432/materiafusiondb?sslmode=disable
API_TIMEOUT_SECONDS=5
API_CALLS_ALLOWED_PER_SECOND=1

Den vollständigen Handler-Code finden Sie hier.

Swagger-UI- und OpenAPI-Definitionsdokument

Fügen Sie die Swagger-Bibliothek hinzu:

go get github.com/joho/godotenv

Kommentieren Sie in „routes.go“ die Swagger-Zeile aus und fügen Sie den Import hinzu:

// At the beginning of main():
err := godotenv.Load(".env") // Loads environment variables from .env file
if err != nil { // This will be true in prod, but that's fine.
  fmt.Println("Error loading .env file")
}


// Modify config struct:
type config struct {
  baseURL string
  db      struct {
    dsn string
  }
  httpPort                 int
  apiTimeout               int
  apiCallsAllowedPerSecond float64
}

// Modify run() to use the new values from .env:
cfg.httpPort = env.GetInt("HTTP_PORT")
cfg.db.dsn = env.GetString("DB_DSN")
cfg.apiTimeout = env.GetInt("API_TIMEOUT_SECONDS")
cfg.apiCallsAllowedPerSecond = float64(env.GetInt("API_CALLS_ALLOWED_PER_SECOND"))

// cfg.baseURL = env.GetString("BASE_URL") - not required

Fügen Sie in den Handler-, DTO- und Modelldateien Kommentare für die Swagger-Dokumentation hinzu. Hier finden Sie alle Optionen.

Führen Sie im Terminal Folgendes aus:

go get github.com/didip/tollbooth

Dadurch wird ein Ordner „api/docs“ erstellt, dessen Definition für Go, JSON und YAML verfügbar ist.

Um es zu testen, starten Sie den lokalen Server und öffnen Sie http://localhost:4444/docs.


Endgültige Ordnerstruktur:

func (app *application) contentTypeCheck(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.Header.Get("Content-Type") != "application/json" {
   app.unsupportedMediaType(w, r)

   return
  }
  next.ServeHTTP(w, r)
 })
}


func (app *application) rateLimiter(next http.Handler) http.Handler {
 limiter := tollbooth.NewLimiter(app.config.apiCallsAllowedPerSecond, nil)
 limiter.SetIPLookups([]string{"X-Real-IP", "X-Forwarded-For", "RemoteAddr"})

 return tollbooth.LimitHandler(limiter, next)
}


func (app *application) apiTimeout(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  timeoutDuration := time.Duration(app.config.apiTimeout) * time.Second

  ctx, cancel := context.WithTimeout(r.Context(), timeoutDuration)
  defer cancel()

  r = r.WithContext(ctx)

  done := make(chan struct{})

  go func() {
   next.ServeHTTP(w, r)
   close(done)
  }()

  select {
  case <-done:
   return
  case <-ctx.Done():
   app.gatewayTimeout(w, r)
   return
  }
 })
}

3. Einrichten der Remote-PostgreSQL-Instanz in CockroachDB

  1. Verwenden Sie die Schritte von hier.
  2. Nachdem Sie das Zertifikat erstellt haben, erstellen Sie /certs/root.crt im Projekt und fügen Sie das Zertifikat dort hinzu. Wir werden später in der Google Run-Konfiguration auf diese Datei verweisen.
  3. ACHTUNG!Wir verschieben diesen Ordner NICHT in das Remote-Repository. Fügen Sie den Ordner „certs/“ zu .gitignore hinzu. Wir erstellen das Zertifikat nur lokal, um die Verbindung zu testen, wenn Sie dies wünschen.
  4. Wenn Sie nun zu CockroachDB → Dashboard → Linkes Menü → Datenbanken gehen, sollten Sie die von Ihnen erstellte Datenbank sehen können.

Migration

Führen Sie von Ihrer lokalen DB-Instanz aus Folgendes aus:

brew install postgresql@17
  1. Gehen Sie zu CockroachDB → Linkes Menü → Migrationen → Schema hinzufügen → Ziehen Sie die SQL-Datei, die Sie gerade erhalten haben. Alle Schritte werden ausgeführt, mit Ausnahme des Einfügens der Tabellendaten. Außerdem wird Ihnen eine Liste der ausgeführten Schritte angezeigt.
  2. Zum Zeitpunkt des Schreibens dieses Artikels unterstützt die PostgreSQL-Instanz in CockroachDB keine Anweisungen wie IMPORT INTO. Also musste ich eine INSERT-Anweisung in einer lokalen SQL-Datei für 270 Zeilen erstellen (die wir aus der pg_dump-Ausgabe ableiten können, die wir gerade erhalten haben).
  3. Melden Sie sich bei der Remote-Instanz an und führen Sie die SQL-Datei aus.

Anmelden bei der Remote-Instanz:

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb

4. Stellen Sie eine Google Cloud Run-Instanz bereit

  1. Erstellen Sie eine Docker-Datei wie diese.
  2. Gehen Sie zu Google Cloud Run und erstellen Sie ein neues Projekt für die API.
  3. Dienst erstellen → Kontinuierliche Bereitstellung aus einem RepoEINRICHTUNG MIT CLOUD BUILDRepository-Anbieter = Github → Wählen Sie Ihr Repo → Build-Typ = Dockerfile → Speichern.
  4. Authentifizierung = Unauthentifizierungsaufrufe zulassen.
  5. Die meisten Standardeinstellungen sollten so wie sie sind in Ordnung sein.
  6. Scrollen Sie nach unten zu Container → Containerhafen = 4444.
  7. Wählen Sie die Registerkarte Variablen und Geheimnisse und fügen Sie dieselben Umgebungsvariablen hinzu, die wir in unserer lokalen .env-Datei haben.

Werte:

  1. HTTP_PORT = 4444
  2. DB_DSN = ?sslmode=verify-full&sslrootcert=/app/certs/root.crt
  3. API_TIMEOUT_SECONDS = 5
  4. API_CALLS_ALLOWED_PER_SECOND = 1

Verwendung von Google Secret Manager für das Zertifikat

Das letzte Puzzleteil.

  1. Nach Secret Manager suchen → Secret erstellen → Name = „DB_CERT“ → Das .crt-Zertifikat der CockroachDB hochladen.
  2. In Cloud Run → (Ihrem Dienst) → Klicken Sie auf Kontinuierliche Bereitstellung bearbeiten → Scrollen Sie nach unten zu Konfiguration → Editor öffnen.
  3. Fügen Sie dies als ersten Schritt hinzu:
-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;

Dadurch erstellt Cloud Build die Datei certs/root.crt in unserem Projekt, bevor der Build beginnt, sodass die Docker-Datei Zugriff darauf hat, auch wenn wir sie nie in unser Github-Repository verschoben haben.


Und das ist es. Versuchen Sie, einen Commit zu pushen, und prüfen Sie, ob der Build ausgelöst wird. Das Cloud Run-Dashboard zeigt die URL Ihres gehosteten Go-Servers an.


Für Fragen im Zusammenhang mit „Warum haben Sie X und nicht Y gemacht?“ Lesen Sie dies.

Für alles andere, was Sie wissen oder besprechen möchten, klicken Sie hier oder kommentieren Sie unten.

Das obige ist der detaillierte Inhalt vonErstellen einer API mit Go, PostgreSQL, Google Cloud und CockroachDB. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn