Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiments with Google API Management APIs #245

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions cmd/zero/DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Zero

Simple API management without a gateway. Instead of using a gateway, we will
make direct calls to service control APIs from within our API server.

Is this a good idea?

Pros:

- simple (no proxies to set up and manage)
- inexpensive (no additional sidecars to operate)
- runs anywhere (even when you run your server locally)

Cons:

- requires changes to your application
- hard to govern, there may be uncontrolled APIs that leak information

## Demonstration

### Preparation

#### You need a domain

To register a service with service manager, a domain is required.

##### Use a domain you control

We can register a domain with a registrar and prove to Google that we own it,
and then we can create services on that domain or any subdomain.
“example1.timbx.me”

##### Get a domain from App Engine

Alternately, we can use Google App Engine to get a domain that we can use. App
Engine apps are hosted at <appname>.appspot.com, where <appname> is usually the
project id.

- create an app engine app for your project
- this will give you “appname.appspot.com”. For example, for my project, named
“nerdvana”, my domain name is “nerdvana.appspot.com”
- we can use this for our service name.
- we can also use subdomains of this domain.

#### Create OAuth credentials

These will be used to call the servicemanagement API

store them in ~/.config/zero/credentials.json

We could also use a service account for this.

#### Set up the CLI

create ~/.config/zero/zero.yaml for general configuration

```
serviceName: nerdvana.appspot.com
serviceConfig: 2023-10-06r1
apiKey: XXX-REDACTED-XXX
producerProject: nerdvana
consumerProject: nerdvana
summary: "Namaste"
title: "Nerdvana"
```

### Service Management

#### Create your service

Create your service with a call to the service management API

View the service in the endpoints console

#### Configure your service

Create your service config -- notice that we want to specify some things:

- name / version
- description
- operations

#### Rollout your configuration

Rollout your service config

Verify the rollout in the endpoints console

### Service Control

Create a service account to call the servicecontrol API

Call the check service

Call the check/allocatequota/report methods

### The Sample API

TODO

## Capabilities and Limitations

TODO

## Using the Service Management API to build an API Catalog

Get a list of managed services and the configurations for each. Each
configuration includes the list of operations that the API supports. These
lists can be used to build an index of APIs being provided by your Google
project.

Not all APIs -- these are just the formally registered ones. Other indicators
of APIs are Load Balancers, Cloud Run endpoints, and GKE ingresses.
25 changes: 25 additions & 0 deletions cmd/zero/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# zero

This directory contains an experimental command-line tool that explores the
[Google Service Infrastructure](https://cloud.google.com/service-infrastructure/docs/overview).

This tool is temporarily named `zero` to indicate that this is a minimalist
effort to explore the Service Infrastructure APIs out of the context of an API
gateway.

Areas to explore include:

- Automatically creating managed services and manage service configurations for
APIs in an API registry.
- Automatically importing information into an API registry from the
[Service Management API](https://cloud.google.com/service-infrastructure/docs/service-management/getting-started).
- Directly calling the
[Service Control API](https://cloud.google.com/service-infrastructure/docs/service-control/getting-started)
as an alternative to using an API proxy for the most basic API management
needs.

Custom components used by the tool are in the `pkg` directory. Implementations
of CLI subcommands are in the `cmd` directory.

Code is published for transparency and community review with no promises of
support or continued existence.
34 changes: 34 additions & 0 deletions cmd/zero/cmd/apikeys/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apikeys

import (
"github.com/apigee/registry-experimental/cmd/zero/cmd/apikeys/operations"
"github.com/spf13/cobra"
)

func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "api-keys",
Short: "API key management",
}
cmd.AddCommand(createCmd())
cmd.AddCommand(deleteCmd())
cmd.AddCommand(getCmd())
cmd.AddCommand(getKeyStringCmd())
cmd.AddCommand(listCmd())
cmd.AddCommand(operations.Cmd())
return cmd
}
73 changes: 73 additions & 0 deletions cmd/zero/cmd/apikeys/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apikeys

import (
"context"
"fmt"

"github.com/apigee/registry-experimental/cmd/zero/pkg/config"
"github.com/apigee/registry-experimental/cmd/zero/pkg/patch"
"github.com/spf13/cobra"
"google.golang.org/api/apikeys/v2"
"google.golang.org/api/option"
)

func createCmd() *cobra.Command {
var output string
var producerProject string
cmd := &cobra.Command{
Use: "create KEYID SERVICE",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client, err := config.GetClient(ctx)
if err != nil {
return err
}
apikey := &apikeys.V2Key{
Restrictions: &apikeys.V2Restrictions{
ApiTargets: []*apikeys.V2ApiTarget{
{Service: args[1]},
},
},
}
srv, err := apikeys.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return err
}
parent := fmt.Sprintf("projects/%s/locations/global", producerProject)
keyId := args[0]
item, err := apikeys.NewProjectsLocationsKeysService(srv).
Create(parent, apikey).
KeyId(keyId).
Do()
if err != nil {
return err
}
bytes, err := patch.MarshalAndWrap(item, "Operation", item.Name, output)
if err != nil {
return err
}
if _, err := cmd.OutOrStdout().Write(bytes); err != nil {
return err
}
return nil
},
}
cmd.Flags().StringVarP(&output, "output", "o", "yaml", "Output format. One of: (yaml, json).")
cmd.Flags().StringVarP(&producerProject, "project", "p", "", "Producer project.")
return cmd
}
62 changes: 62 additions & 0 deletions cmd/zero/cmd/apikeys/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apikeys

import (
"context"

"github.com/apigee/registry-experimental/cmd/zero/pkg/config"
"github.com/apigee/registry-experimental/cmd/zero/pkg/patch"
"google.golang.org/api/apikeys/v2"
"google.golang.org/api/option"

"github.com/spf13/cobra"
)

func deleteCmd() *cobra.Command {
var output string
cmd := &cobra.Command{
Use: "delete APIKEY",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client, err := config.GetClient(ctx)
if err != nil {
return err
}
apikey := args[0]
srv, err := apikeys.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return err
}
item, err := apikeys.NewProjectsLocationsKeysService(srv).
Delete(apikey).
Do()
if err != nil {
return err
}
bytes, err := patch.MarshalAndWrap(item, "Operation", item.Name, output)
if err != nil {
return err
}
if _, err := cmd.OutOrStdout().Write(bytes); err != nil {
return err
}
return nil
},
}
cmd.Flags().StringVarP(&output, "output", "o", "yaml", "Output format. One of: (yaml, json).")
return cmd
}
62 changes: 62 additions & 0 deletions cmd/zero/cmd/apikeys/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apikeys

import (
"context"

"github.com/apigee/registry-experimental/cmd/zero/pkg/config"
"github.com/apigee/registry-experimental/cmd/zero/pkg/patch"
"google.golang.org/api/apikeys/v2"
"google.golang.org/api/option"

"github.com/spf13/cobra"
)

func getCmd() *cobra.Command {
var output string
cmd := &cobra.Command{
Use: "get APIKEY",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client, err := config.GetClient(ctx)
if err != nil {
return err
}
apikey := args[0]
srv, err := apikeys.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return err
}
item, err := apikeys.NewProjectsLocationsKeysService(srv).
Get(apikey).
Do()
if err != nil {
return err
}
bytes, err := patch.MarshalAndWrap(item, "ApiKey", item.Name, output)
if err != nil {
return err
}
if _, err := cmd.OutOrStdout().Write(bytes); err != nil {
return err
}
return nil
},
}
cmd.Flags().StringVarP(&output, "output", "o", "yaml", "Output format. One of: (yaml, json).")
return cmd
}
Loading
Loading