Add the function * constraints * environment variables * secrets * requests and limits This can be tested as follows: Create a test cluster using KinD ```sh kind create cluster --config=cluster.yaml arkade install openfaas -a=false --function-pull-policy=IfNotPresent --wait faas-cli store deploy nodeinfo --annotation sample=true --label status=418 ``` Now deploy a sample function ```sh $ faas-cli describe nodeinfo Name: nodeinfo Status: Ready Replicas: 1 Available replicas: 1 Invocations: 0 Image: ghcr.io/openfaas/nodeinfo:latest Function process: <default> URL: http://127.0.0.1:8080/function/nodeinfo Async URL: http://127.0.0.1:8080/async-function/nodeinfo Labels faas_function : nodeinfo status : 418 Annotations sample : true prometheus.io.scrape : false Constraints <none> Environment <none> Secrets <none> Requests <none> Limits <none> ``` Now we add some more interesting values, like a secret and env variables. ```sh $ faas-cli secret create db-password --from-literal=password Creating secret: db-password. Created: 202 Accepted $ faas-cli store deploy nodeinfo --annotation sample=true --label status=418 --secret db-password --env db-user=postgres --env db-host=rds.aws.example.com Deployed. 202 Accepted. URL: http://127.0.0.1:8080/function/nodeinfo $ faas-cli describe nodeinfo Name: nodeinfo Status: Ready Replicas: 1 Available replicas: 1 Invocations: 0 Image: ghcr.io/openfaas/nodeinfo:latest Function process: <default> URL: http://127.0.0.1:8080/function/nodeinfo Async URL: http://127.0.0.1:8080/async-function/nodeinfo Labels: faas_function: nodeinfo status: 418 uid: 736815901 Annotations: prometheus.io.scrape: false sample: true Constraints: <none> Environment: <none> Secrets: - db-password Requests: <none> Limits: <none> ``` To see how multiple Secrets and the Requests and Limits are rendered, use ```yaml version: 1.0 provider: name: openfaas gateway: http://127.0.0.1:8080 functions: nodeinfo: lang: Dockerfile image: ghcr.io/openfaas/nodeinfo:latest secrets: - cache-password - db-password environment: db-host: rds.aws.example.com cache-host: redis.aws.example.com labels: status: 481 annotations: sample: "true" requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mi ``` then ```sh $ faas-cli secret create cache-password --from-literal=password Creating secret: cache-password. Created: 202 Accepted $ faas-cli deploy Deploying: nodeinfo. Deployed. 202 Accepted. URL: http://127.0.0.1:8080/function/nodeinfo $ faas-cli describe nodeinfo Name: nodeinfo Status: Ready Replicas: 1 Available replicas: 1 Invocations: 0 Image: ghcr.io/openfaas/nodeinfo:latest Function process: <default> URL: http://127.0.0.1:8080/function/nodeinfo Async URL: http://127.0.0.1:8080/async-function/nodeinfo Labels: faas_function: nodeinfo status: 481 uid: 679245186 Annotations: prometheus.io.scrape: false sample: true Constraints: <none> Environment: <none> Secrets: - cache-password - db-password Requests: CPU: 100m Memory: 128Mi Limits: CPU: 200m Memory: 256Mi ``` Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
212 lines
5.6 KiB
Go
212 lines
5.6 KiB
Go
// Copyright (c) OpenFaaS Author(s) 2018. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"text/tabwriter"
|
|
|
|
"github.com/openfaas/faas-cli/proxy"
|
|
"github.com/openfaas/faas-cli/schema"
|
|
"github.com/openfaas/faas-cli/stack"
|
|
"github.com/openfaas/faas-provider/types"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func init() {
|
|
describeCmd.Flags().StringVar(&functionName, "name", "", "Name of the function")
|
|
describeCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
|
|
describeCmd.Flags().BoolVar(&tlsInsecure, "tls-no-verify", false, "Disable TLS validation")
|
|
describeCmd.Flags().BoolVar(&envsubst, "envsubst", true, "Substitute environment variables in stack.yml file")
|
|
describeCmd.Flags().StringVarP(&token, "token", "k", "", "Pass a JWT token to use instead of basic auth")
|
|
describeCmd.Flags().StringVarP(&functionNamespace, "namespace", "n", "", "Namespace of the function")
|
|
|
|
faasCmd.AddCommand(describeCmd)
|
|
}
|
|
|
|
var describeCmd = &cobra.Command{
|
|
Use: "describe FUNCTION_NAME [--gateway GATEWAY_URL]",
|
|
Short: "Describe an OpenFaaS function",
|
|
Long: `Display details of an OpenFaaS function`,
|
|
Example: `faas-cli describe figlet
|
|
faas-cli describe env --gateway http://127.0.0.1:8080
|
|
faas-cli describe echo -g http://127.0.0.1.8080`,
|
|
PreRunE: preRunDescribe,
|
|
RunE: runDescribe,
|
|
}
|
|
|
|
func preRunDescribe(cmd *cobra.Command, args []string) error {
|
|
return nil
|
|
}
|
|
|
|
func runDescribe(cmd *cobra.Command, args []string) error {
|
|
if len(args) < 1 {
|
|
return fmt.Errorf("please provide a name for the function")
|
|
}
|
|
var yamlGateway string
|
|
var services stack.Services
|
|
functionName = args[0]
|
|
|
|
if len(yamlFile) > 0 {
|
|
parsedServices, err := stack.ParseYAMLFile(yamlFile, regex, filter, envsubst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if parsedServices != nil {
|
|
services = *parsedServices
|
|
yamlGateway = services.Provider.GatewayURL
|
|
}
|
|
}
|
|
gatewayAddress := getGatewayURL(gateway, defaultGateway, yamlGateway, os.Getenv(openFaaSURLEnvironment))
|
|
cliAuth, err := proxy.NewCLIAuth(token, gatewayAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
transport := GetDefaultCLITransport(tlsInsecure, &commandTimeout)
|
|
cliClient, err := proxy.NewClient(cliAuth, gatewayAddress, transport, &commandTimeout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
function, err := cliClient.GetFunctionInfo(ctx, functionName, functionNamespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//To get correct value for invocation count from /system/functions endpoint
|
|
functionList, err := cliClient.ListFunctions(ctx, functionNamespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var invocationCount int
|
|
for _, fn := range functionList {
|
|
if fn.Name == function.Name {
|
|
invocationCount = int(fn.InvocationCount)
|
|
break
|
|
}
|
|
}
|
|
|
|
var status = "Not Ready"
|
|
if function.AvailableReplicas > 0 {
|
|
status = "Ready"
|
|
}
|
|
|
|
url, asyncURL := getFunctionURLs(gatewayAddress, functionName, functionNamespace)
|
|
|
|
funcDesc := schema.FunctionDescription{
|
|
FunctionStatus: function,
|
|
Status: status,
|
|
InvocationCount: int(invocationCount),
|
|
URL: url,
|
|
AsyncURL: asyncURL,
|
|
}
|
|
|
|
printFunctionDescription(funcDesc)
|
|
|
|
return nil
|
|
}
|
|
|
|
func getFunctionURLs(gateway string, functionName string, functionNamespace string) (string, string) {
|
|
gateway = strings.TrimRight(gateway, "/")
|
|
|
|
url := gateway + "/function/" + functionName
|
|
asyncURL := gateway + "/async-function/" + functionName
|
|
|
|
if functionNamespace != "" {
|
|
url += "." + functionNamespace
|
|
asyncURL += "." + functionNamespace
|
|
}
|
|
|
|
return url, asyncURL
|
|
}
|
|
|
|
func printFunctionDescription(funcDesc schema.FunctionDescription) {
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
|
|
process := "<default>"
|
|
if funcDesc.EnvProcess != "" {
|
|
process = funcDesc.EnvProcess
|
|
}
|
|
fmt.Fprintln(w, "Name:\t "+funcDesc.Name)
|
|
fmt.Fprintln(w, "Status:\t "+funcDesc.Status)
|
|
fmt.Fprintln(w, "Replicas:\t "+strconv.Itoa(int(funcDesc.Replicas)))
|
|
fmt.Fprintln(w, "Available replicas:\t "+strconv.Itoa(int(funcDesc.AvailableReplicas)))
|
|
fmt.Fprintln(w, "Invocations:\t "+strconv.Itoa(funcDesc.InvocationCount))
|
|
fmt.Fprintln(w, "Image:\t "+funcDesc.Image)
|
|
fmt.Fprintln(w, "Function process:\t "+process)
|
|
fmt.Fprintln(w, "URL:\t "+funcDesc.URL)
|
|
fmt.Fprintln(w, "Async URL:\t "+funcDesc.AsyncURL)
|
|
printMap(w, "Labels", *funcDesc.Labels)
|
|
printMap(w, "Annotations", *funcDesc.Annotations)
|
|
printList(w, "Constraints", funcDesc.Constraints)
|
|
printMap(w, "Environment", funcDesc.EnvVars)
|
|
printList(w, "Secrets", funcDesc.Secrets)
|
|
printResources(w, "Requests", funcDesc.Requests)
|
|
printResources(w, "Limits", funcDesc.Limits)
|
|
if funcDesc.Usage != nil {
|
|
fmt.Println()
|
|
|
|
fmt.Fprintf(w, "RAM:\t %.2f MB\n", (funcDesc.Usage.TotalMemoryBytes / 1024 / 1024))
|
|
cpu := funcDesc.Usage.CPU
|
|
if cpu < 0 {
|
|
cpu = 1
|
|
}
|
|
fmt.Fprintf(w, "CPU:\t %.0f Mi\n", (cpu))
|
|
}
|
|
|
|
w.Flush()
|
|
}
|
|
|
|
func printMap(w *tabwriter.Writer, name string, m map[string]string) {
|
|
fmt.Fprintf(w, name+":")
|
|
|
|
if len(m) == 0 {
|
|
fmt.Fprintln(w, " \t <none>")
|
|
return
|
|
}
|
|
|
|
for key, value := range m {
|
|
fmt.Fprintln(w, " \t "+key+": "+value)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func printList(w *tabwriter.Writer, name string, data []string) {
|
|
fmt.Fprintf(w, name+":")
|
|
|
|
if len(data) == 0 {
|
|
fmt.Fprintln(w, " \t <none>")
|
|
return
|
|
}
|
|
|
|
for _, value := range data {
|
|
fmt.Fprintln(w, " \t - "+value)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func printResources(w *tabwriter.Writer, name string, data *types.FunctionResources) {
|
|
fmt.Fprintf(w, name+":")
|
|
|
|
if data == nil {
|
|
fmt.Fprintln(w, " \t <none>")
|
|
return
|
|
}
|
|
|
|
fmt.Fprintln(w, " \t CPU: "+data.CPU)
|
|
fmt.Fprintln(w, " \t Memory: "+data.Memory)
|
|
|
|
return
|
|
}
|