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

Sample config file generation #43

Merged
merged 8 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
52 changes: 52 additions & 0 deletions bin/build_templates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash

# Copyright 2020 Google LLC
#
# 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.

#
# If you change any templates:
# 1. run this script to generate templates.go
# 2. run `go mod tidy` to remove the go-bindata dep from your mod and sum files
# 3. check in your changes
#

SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOTDIR="$(dirname "$SCRIPTPATH")"

if [[ `command -v go-bindata` == "" ]]; then
echo "go-bindata not installed, installing..."
go get -u github.com/go-bindata/go-bindata/...
fi

TEMP_DIR=$(mktemp -d)
TEMPLATES_SOURCE_DIR="${ROOTDIR}/templates"
TEMPLATES_DIR="${TEMP_DIR}/templates"

if [ ! -d "${TEMPLATES_DIR}" ]; then
mkdir -p "${TEMPLATES_DIR}"
fi


cp -r "${TEMPLATES_SOURCE_DIR}/native" $TEMPLATES_DIR
cp -r "${TEMPLATES_SOURCE_DIR}/istio-1.6" $TEMPLATES_DIR
cp -r "${TEMPLATES_SOURCE_DIR}/istio-1.7" $TEMPLATES_DIR

# create resource
RESOURCE_FILE="${ROOTDIR}/templates/templates.go"
echo "building ${RESOURCE_FILE}"
cd ${TEMP_DIR}
echo $(ls "${TEMP_DIR}/templates")
go-bindata -nomemcopy -pkg "templates" -prefix "templates" -o "${RESOURCE_FILE}" templates/...

echo "done"
6 changes: 3 additions & 3 deletions cmd/provision/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (p *provision) checkRuntimeVersion(config *server.Config, client *http.Clie
}

func (p *provision) encodeUDCAEndpoint(config *server.Config, verbosef shared.FormatFn) {
config.Analytics.FluentdEndpoint = fmt.Sprintf(fluentdInternalEncodedFormat, envScopeEncodedName(p.Org, p.Env), p.Namespace)
config.Analytics.FluentdEndpoint = fmt.Sprintf(fluentdInternalEncodedFormat, EnvScopeEncodedName(p.Org, p.Env), p.Namespace)
verbosef("UDCA endpoint encoded")
}

Expand All @@ -213,8 +213,8 @@ func shortSha(s string) string {
return sha[:7]
}

// envScopeEncodedName returns the encoded resource name to avoid the 63 chars limit
func envScopeEncodedName(org, env string) string {
// EnvScopeEncodedName returns the encoded resource name to avoid the 63 chars limit
func EnvScopeEncodedName(org, env string) string {
sha := shortSha(fmt.Sprintf("%s:%s", org, env))
return fmt.Sprintf("%s-%s-%s", shortName(org), shortName(env), sha)
}
2 changes: 1 addition & 1 deletion cmd/provision/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestEncodedName(t *testing.T) {
org := "thisorgnameistoolong"
env := "thisenvnameisevenworse"
want := "thisorgnameisto-thisenvnameisev-fe4ea4e"
got := envScopeEncodedName(org, env)
got := EnvScopeEncodedName(org, env)
if got != want {
t.Errorf("encoding is incorrect, want %s, got %s", want, got)
}
Expand Down
213 changes: 213 additions & 0 deletions cmd/samples/samples.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// Copyright 2020 Google LLC
//
// 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 samples

import (
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"strings"
"text/template"

"github.com/apigee/apigee-remote-service-cli/cmd/provision"
"github.com/apigee/apigee-remote-service-cli/shared"
"github.com/apigee/apigee-remote-service-cli/templates"
"github.com/apigee/apigee-remote-service-envoy/server"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

const (
nativeTemplateDir = "native"
istioTemplateDir = "istio-1.6"
)

type samples struct {
*shared.RootArgs
isNative bool
outDir string
overwrite bool
RuntimeHost string
TargetService targetService
TLS tls
EncodedName string
}

type targetService struct {
Name string
Host string
Prefix string
}

type tls struct {
Dir string
Key string
Crt string
}

// Cmd returns base command
func Cmd(rootArgs *shared.RootArgs, printf shared.FormatFn) *cobra.Command {
s := &samples{
RootArgs: rootArgs,
TargetService: targetService{},
TLS: tls{},
}

c := &cobra.Command{
Use: "samples",
Short: "Managing sample configuration files for remote-service deployment",
Long: `Managing sample configuration files for remote-service deployment`,
Args: cobra.NoArgs,
}

c.AddCommand(cmdCreateSampleConfig(s, printf))

return c
}

func cmdCreateSampleConfig(s *samples, printf shared.FormatFn) *cobra.Command {
c := &cobra.Command{
Use: "create",
Short: "Create sample configuration files for native envoy or istio",
Long: `Create sample configuration files for native envoy or istio. A valid config
yaml file generated through provisioning is required via --config/-c. Files will be in
the directory specified via --out (default ./samples).
In the case of native envoy, it takes the host of the target service, the desired name
for its cluster and optionally the path prefix for matching. It also sets custom SSL
connection from the envoy to the remote-service cluster if a folder containing tls.key
and tls.crt is provided via --tls/-t.
In the case of istio where envoy proxy acts as sidecars, if the target is unspecified,
the httpbin example will be generated. Otherwise, users are responsible for preparing
files related to deployment of their target services.`,
Args: cobra.NoArgs,

RunE: func(cmd *cobra.Command, _ []string) error {
err := s.loadConfig()
if err != nil {
return errors.Wrap(err, "loading config yaml file")
}
return errors.Wrap(s.createSampleConfigs(printf), "creating sample config files")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should emit a final message when it finishes successfully. Maybe remind them of the next steps: label the default namespace and run kubectl apply on the directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

},
}

c.Flags().StringVarP(&s.ConfigPath, "config", "c", "", "Path to Apigee Remote Service config file")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

de-capitalize - all the other descriptions start lowercase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the config file description in the root.go is identical to this. I am going to change both to lower case.

c.Flags().BoolVarP(&s.isNative, "native", "", false, "generate config for native envoy (otherwise assuming istio)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder... could we pass a template name instead? So, for example, a user could pass in -t native or -t istio-1.7 and have access to all options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.

c.Flags().BoolVarP(&s.overwrite, "force", "f", false, "force overwriting existing directory")
c.Flags().StringVarP(&s.outDir, "out", "", "./samples", "directory to create config files within")
c.Flags().StringVarP(&s.TargetService.Name, "name", "n", "httpbin", "target service name")
c.Flags().StringVarP(&s.TargetService.Host, "host", "", "httpbin.org", "target service host")
c.Flags().StringVarP(&s.TargetService.Prefix, "prefix", "p", "/", "target service prefix")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove path rewrite because of the confusing issues around ordering. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

Copy link
Contributor Author

@rockspore rockspore Aug 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I already removed the prefix rewriting. This flag is taking the path for matching, i.e., for the Envoy router to take the request to the desired upstream cluster. Should we remove that as well? I think it probably should stay.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Hmm. But the only benefit would be to allow other endpoints to not be captured by the matcher, correct? Is that important for sample generation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. All traffics are meant for the only target in the sample. Let me remove it then.

c.Flags().StringVarP(&s.TLS.Dir, "tls", "t", "", "directory for tls key and crt")

_ = c.MarkFlagRequired("config")

return c
}

func (s *samples) loadConfig() error {
s.ServerConfig = &server.Config{}
err := s.ServerConfig.Load(s.ConfigPath, "")
if err != nil {
return err
}

s.RuntimeBase = strings.Split(s.ServerConfig.Tenant.RemoteServiceAPI, "/remote-service")[0]
url, err := url.Parse(s.RuntimeBase)
if err != nil {
return err
}
s.RuntimeHost = url.Hostname()
s.Org = s.ServerConfig.Tenant.OrgName
s.Env = s.ServerConfig.Tenant.EnvName
s.Namespace = s.ServerConfig.Global.Namespace
s.EncodedName = provision.EnvScopeEncodedName(s.Org, s.Env)

if s.TLS.Dir != "" {
s.TLS.Key = path.Join(s.TLS.Dir, "tls.key")
s.TLS.Crt = path.Join(s.TLS.Dir, "tls.crt")
}

return nil
}

func (s *samples) createSampleConfigs(printf shared.FormatFn) error {
_, err := ioutil.ReadDir(s.outDir)
if err != nil {
if err := os.Mkdir(s.outDir, 0755); err != nil {
return err
}
} else if s.overwrite {
printf("overwriting the existing directory...")
} else {
return fmt.Errorf("output directory already exists")
}
if s.isNative {
printf("generating the configuration file for native envoy proxy...")
return s.createConfig(nativeTemplateDir, printf)
}
printf("generating configuration files envoy as sidecars...")
return s.createConfig(istioTemplateDir, printf)
}

func (s *samples) createConfig(templateDir string, printf shared.FormatFn) error {
tempDir, err := ioutil.TempDir("", "apigee")
if err != nil {
return errors.Wrap(err, "creating temp dir")
}
defer os.RemoveAll(tempDir)

err = getTemplates(tempDir, templateDir)
if err != nil {
return errors.Wrap(err, "getting templates")
}
path := path.Join(tempDir, templateDir)
templates, err := ioutil.ReadDir(path)
if err != nil {
return errors.Wrap(err, "getting templates directory")
}
for _, f := range templates {
if f.Name() == "httpbin.yaml" && s.TargetService.Name != "httpbin" {
continue
}
err := s.createConfigYaml(path, f.Name(), printf)
if err != nil {
return err
}
}
return nil
}

func (s *samples) createConfigYaml(dir string, name string, printf shared.FormatFn) error {
tmpl, err := template.New(name).ParseFiles(path.Join(dir, name))
if err != nil {
return err
}
f, err := os.OpenFile(path.Join(s.outDir, name), os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
return err
}
printf("generating %s...", name)
return tmpl.Execute(f, s)
}

// getTemplates unzips the templates to the tempDir/templates and returns the directory
func getTemplates(tempDir string, name string) error {
if err := templates.RestoreAssets(tempDir, name); err != nil {
return errors.Wrapf(err, "restoring asset %s", name)
}
return nil
}
Loading