image/resources/netapp-exports/main.go (143 lines of code) (raw):
/*
Copyright 2022 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
https://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 main
import (
"bufio"
"context"
"flag"
"fmt"
"io"
"net/http"
"netapp-exports/internal/opt"
"os"
"sort"
)
func main() {
var (
config *Config
configFile string
passwordFile string
caFile string
insecure bool
err error
)
server := &NetAppServer{
TLS: &TLSConfig{},
}
secret := GCPSecret{}
flags := flag.CommandLine
opts := opt.NewOptSet(flags)
flags.StringVar(&configFile, "config", "", "Config file")
opts.StringVar(&server.Host, "host", "NETAPP_HOST", "NetApp Host")
opts.StringVar(&server.URL, "url", "NETAPP_URL", "NetApp URL")
opts.StringVar(&server.User, "user", "NETAPP_USER", "NetApp User")
opts.StringVar(&server.Password, "password", "NETAPP_PASSWORD", "NetApp password")
opts.StringVar(&passwordFile, "password-file", "NETAPP_PASSWORD_FILE", "File containing NetApp password")
opts.StringVar(&secret.Project, "secret-project", "NETAPP_SECRET_PROJECT", "GCP Secret name containing NetApp password")
opts.StringVar(&secret.Name, "secret-name", "NETAPP_SECRET", "GCP Secret name containing NetApp password")
opts.StringVar(&secret.Version, "secret-version", "NETAPP_SECRET_VERSION", "GCP Secret version containing NetApp password")
opts.StringVar(&caFile, "ca", "NETAPP_CA", "Path to NetApp SSL root certificate in PEM format")
flags.BoolVar(&insecure, "insecure", false, "Allow insecure TLS connections (ignore server certificate)")
opts.BoolVar(&server.TLS.AllowCommonName, "allow-common-name", "NETAPP_ALLOW_COMMON_NAME", "Allow using the Common Name (CN) field from the certificate's subject. By default only Subject Alternate Names (SANs) are supported.")
opts.Parse(os.Args[1:])
if configFile != "" {
config, err = parseConfigFile(configFile)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Could not read config file %s: %v\n", configFile, err)
os.Exit(1)
}
} else {
if passwordFile != "" {
password, err := readFirstLine(passwordFile)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Could not read password file %s: %s\n", passwordFile, err)
os.Exit(1)
}
server.Password = password
}
if caFile != "" {
cert, err := os.ReadFile(caFile)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Could not read CA file %s: %s\n", caFile, err)
os.Exit(1)
}
server.TLS.CACertificate = string(cert)
}
if secret.Name != "" {
server.SecurePassword = &NetAppPassword{
GCPSecret: &secret,
}
}
err = server.validate()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
os.Exit(1)
}
config = &Config{
Servers: []*NetAppServer{server},
}
}
if insecure {
fmt.Fprint(os.Stderr, "WARNING: Using insecure TLS is vulnerable to man in the middle attacks and should only be used for testing.\n")
}
for _, s := range config.Servers {
s.TLS.insecure = insecure
err = listExports(os.Stdout, s)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Could not list exports for %s: %v\n", s.Host, err)
os.Exit(1)
}
}
}
func listExports(w io.Writer, s *NetAppServer) error {
password, err := resolvePassword(s)
if err != nil {
return err
}
transport, err := s.TLS.transport()
if err != nil {
return err
}
client := &http.Client{
Transport: transport,
}
api := &API{
Client: client,
BaseURL: s.URL,
User: s.User,
Password: password,
}
paths, err := api.FetchAll()
if err != nil {
return err
}
sort.Strings(paths)
for _, path := range paths {
fmt.Fprintf(w, "%s %s\n", s.Host, path)
}
return nil
}
func resolvePassword(s *NetAppServer) (string, error) {
if s.Password != "" {
return s.Password, nil
}
password, err := s.SecurePassword.GCPSecret.get(context.Background())
if err != nil {
return "", fmt.Errorf("could not fetch password from Cloud Secret: %w", err)
}
return password, nil
}
func readFirstLine(name string) (string, error) {
f, err := os.Open(name)
if err != nil {
return "", err
}
defer f.Close()
s := bufio.NewScanner(f)
if s.Scan() {
return s.Text(), nil
}
err = s.Err()
if err == nil {
err = fmt.Errorf("failed to read %s", name)
}
return "", err
}