123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- package aws
- // This file contains functions for interacting with AWS Lambda
- // it take a docker image and push it to ECR, create a lambda function and deploy it
- // also, it will create a function url for the lambda function with auth enabled
- import (
- "context"
- "encoding/base64"
- "fmt"
- "strings"
- "github.com/aws/aws-sdk-go-v2/aws"
- "github.com/aws/aws-sdk-go-v2/config"
- "github.com/aws/aws-sdk-go-v2/credentials"
- "github.com/aws/aws-sdk-go-v2/service/ecr"
- "github.com/aws/aws-sdk-go-v2/service/lambda"
- lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
- "github.com/aws/aws-sdk-go-v2/service/sts"
- "github.com/langgenius/dify-plugin-daemon/internal/types/app"
- "github.com/langgenius/dify-plugin-daemon/internal/types/entities"
- "github.com/langgenius/dify-plugin-daemon/internal/utils/log"
- )
- var (
- aws_lambda_config *aws.Config
- lambda_client *lambda.Client
- lambda_account_id string
- )
- // InitLambda initializes the AWS configuration and validates the credentials
- // It takes a pointer to the app.Config struct as an argument
- func InitLambda(app *app.Config) {
- // Check if required AWS Lambda configuration is provided
- if app.AWSLambdaRegion == nil || app.AWSLambdaAccessKey == nil || app.AWSLambdaSecretKey == nil {
- log.Panic("AWSLambdaRegion, AWSLambdaAccessKey, and AWSLambdaSecretKey must be set")
- }
- // Load AWS configuration with provided credentials
- c, err := config.LoadDefaultConfig(
- context.TODO(),
- config.WithRegion(*app.AWSLambdaRegion),
- config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
- *app.AWSLambdaAccessKey,
- *app.AWSLambdaSecretKey,
- "",
- )),
- )
- // Handle error if AWS config loading fails
- if err != nil {
- log.Panic("Failed to load AWS Lambda config: %v", err)
- }
- log.Info("AWS Lambda config loaded")
- // Create STS client to validate AWS credentials
- stsClient := sts.NewFromConfig(c)
- identity, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{})
- if err != nil {
- log.Panic("Failed to validate AWS Lambda credentials: %v", err)
- }
- // Get the account ID
- lambda_account_id = *identity.Account
- // Create the Lambda client
- lambda_client = lambda.NewFromConfig(c)
- log.Info("AWS Lambda credentials validated successfully")
- // Store the AWS configuration globally
- aws_lambda_config = &c
- }
- type LambdaFunction struct {
- FunctionName string
- FunctionARN string
- FunctionURL string
- }
- // PushImageToECR pushes a Docker image to ECR
- func PushImageToECR(ctx context.Context, plugin_runtime entities.PluginRuntimeInterface) (string, error) {
- ecr_client := ecr.NewFromConfig(*aws_lambda_config)
- // Create ECR repository if it doesn't exist
- identity, err := plugin_runtime.Identity()
- if err != nil {
- return "", fmt.Errorf("failed to get plugin identity: %v", err)
- }
- image_name := fmt.Sprintf("dify-plugin-%s-%s", identity, plugin_runtime.Checksum())
- repo_name := fmt.Sprintf("dify-plugin-%s", image_name)
- _, err = ecr_client.CreateRepository(ctx, &ecr.CreateRepositoryInput{
- RepositoryName: aws.String(repo_name),
- })
- if err != nil && !strings.Contains(err.Error(), "RepositoryAlreadyExistsException") {
- return "", fmt.Errorf("failed to create ECR repository: %v", err)
- }
- // Get ECR authorization token
- auth_output, err := ecr_client.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
- if err != nil {
- return "", fmt.Errorf("failed to get ECR authorization token: %v", err)
- }
- if len(auth_output.AuthorizationData) == 0 || auth_output.AuthorizationData[0].AuthorizationToken == nil {
- return "", fmt.Errorf("invalid ECR authorization data")
- }
- auth_token, err := base64.StdEncoding.DecodeString(*auth_output.AuthorizationData[0].AuthorizationToken)
- if err != nil {
- return "", fmt.Errorf("failed to decode ECR authorization token: %v", err)
- }
- // Extract username and password from auth token
- credentials := strings.SplitN(string(auth_token), ":", 2)
- if len(credentials) != 2 {
- return "", fmt.Errorf("invalid ECR credentials format")
- }
- // TODO: Use the extracted credentials to push the Docker image to ECR
- // This step typically involves using a Docker client library or executing Docker CLI commands
- if auth_output.AuthorizationData[0].ProxyEndpoint == nil {
- return "", fmt.Errorf("invalid ECR proxy endpoint")
- }
- return fmt.Sprintf("%s/%s:latest", *auth_output.AuthorizationData[0].ProxyEndpoint, repo_name), nil
- }
- // CreateLambdaFunction creates a Lambda function from an ECR image
- func CreateLambdaFunction(ctx context.Context, plugin_runtime entities.PluginRuntimeInterface, image_uri string) (*LambdaFunction, error) {
- function_name := fmt.Sprintf("dify-plugin-%s", plugin_runtime.Checksum())
- // Get or create the lambda execution role
- role_arn, err := getOrCreateLambdaExecutionRole(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to get or create Lambda execution role: %v", err)
- }
- create_output, err := lambda_client.CreateFunction(ctx, &lambda.CreateFunctionInput{
- FunctionName: aws.String(function_name),
- Role: aws.String(role_arn),
- PackageType: lambdatypes.PackageTypeImage,
- Code: &lambdatypes.FunctionCode{
- ImageUri: aws.String(image_uri),
- },
- })
- if err != nil {
- return nil, fmt.Errorf("failed to create Lambda function: %v", err)
- }
- if create_output.FunctionArn == nil {
- return nil, fmt.Errorf("invalid Lambda function creation output")
- }
- // Create function URL
- url_output, err := lambda_client.CreateFunctionUrlConfig(ctx, &lambda.CreateFunctionUrlConfigInput{
- FunctionName: aws.String(function_name),
- AuthType: lambdatypes.FunctionUrlAuthTypeAwsIam,
- })
- if err != nil {
- return nil, fmt.Errorf("failed to create function URL: %v", err)
- }
- if url_output.FunctionUrl == nil {
- return nil, fmt.Errorf("invalid function URL creation output")
- }
- return &LambdaFunction{
- FunctionName: function_name,
- FunctionARN: *create_output.FunctionArn,
- FunctionURL: *url_output.FunctionUrl,
- }, nil
- }
- // ListLambdaFunctions lists all Lambda functions with the "dify-plugin-" prefix
- func ListLambdaFunctions(ctx context.Context) ([]*LambdaFunction, error) {
- var functions []*LambdaFunction
- var marker *string
- for {
- output, err := lambda_client.ListFunctions(ctx, &lambda.ListFunctionsInput{
- Marker: marker,
- })
- if err != nil {
- return nil, fmt.Errorf("failed to list Lambda functions: %v", err)
- }
- for _, f := range output.Functions {
- if f.FunctionName == nil || f.FunctionArn == nil {
- continue
- }
- if strings.HasPrefix(*f.FunctionName, "dify-plugin-") {
- url_output, err := lambda_client.GetFunctionUrlConfig(ctx, &lambda.GetFunctionUrlConfigInput{
- FunctionName: f.FunctionName,
- })
- if err != nil {
- return nil, fmt.Errorf("failed to get function URL for %s: %v", *f.FunctionName, err)
- }
- if url_output.FunctionUrl == nil {
- return nil, fmt.Errorf("invalid function URL output for %s", *f.FunctionName)
- }
- functions = append(functions, &LambdaFunction{
- FunctionName: *f.FunctionName,
- FunctionARN: *f.FunctionArn,
- FunctionURL: *url_output.FunctionUrl,
- })
- }
- }
- if output.NextMarker == nil {
- break
- }
- marker = output.NextMarker
- }
- return functions, nil
- }
- // GetLambdaFunction retrieves a specific Lambda function by its checksum
- func GetLambdaFunction(ctx context.Context, identity string, checksum string) (*LambdaFunction, error) {
- function_name := fmt.Sprintf("dify-plugin-%s-%s", identity, checksum)
- output, err := lambda_client.GetFunction(ctx, &lambda.GetFunctionInput{
- FunctionName: aws.String(function_name),
- })
- if err != nil {
- return nil, fmt.Errorf("failed to get Lambda function: %v", err)
- }
- if output.Configuration == nil || output.Configuration.FunctionName == nil || output.Configuration.FunctionArn == nil {
- return nil, fmt.Errorf("invalid GetFunction output")
- }
- url_output, err := lambda_client.GetFunctionUrlConfig(ctx, &lambda.GetFunctionUrlConfigInput{
- FunctionName: aws.String(function_name),
- })
- if err != nil {
- return nil, fmt.Errorf("failed to get function URL: %v", err)
- }
- if url_output.FunctionUrl == nil {
- return nil, fmt.Errorf("invalid function URL output")
- }
- return &LambdaFunction{
- FunctionName: *output.Configuration.FunctionName,
- FunctionARN: *output.Configuration.FunctionArn,
- FunctionURL: *url_output.FunctionUrl,
- }, nil
- }
- // UpdateLambdaFunction updates an existing Lambda function with a new image
- func UpdateLambdaFunction(ctx context.Context, plugin_runtime entities.PluginRuntimeInterface, image_uri string) error {
- // Get the function name
- identity, err := plugin_runtime.Identity()
- if err != nil {
- return fmt.Errorf("failed to get plugin identity: %v", err)
- }
- function_name := fmt.Sprintf("dify-plugin-%s-%s", identity, plugin_runtime.Checksum())
- _, err = lambda_client.UpdateFunctionCode(ctx, &lambda.UpdateFunctionCodeInput{
- FunctionName: aws.String(function_name),
- ImageUri: aws.String(image_uri),
- })
- if err != nil {
- return fmt.Errorf("failed to update Lambda function: %v", err)
- }
- return nil
- }
- // DeleteLambdaFunction deletes a Lambda function and its associated function URL
- func DeleteLambdaFunction(ctx context.Context, plugin_runtime entities.PluginRuntimeInterface) error {
- // Get the function name
- identity, err := plugin_runtime.Identity()
- if err != nil {
- return fmt.Errorf("failed to get plugin identity: %v", err)
- }
- function_name := fmt.Sprintf("dify-plugin-%s-%s", identity, plugin_runtime.Checksum())
- // Delete function URL
- _, err = lambda_client.DeleteFunctionUrlConfig(ctx, &lambda.DeleteFunctionUrlConfigInput{
- FunctionName: aws.String(function_name),
- })
- if err != nil {
- return fmt.Errorf("failed to delete function URL: %v", err)
- }
- // Delete Lambda function
- _, err = lambda_client.DeleteFunction(ctx, &lambda.DeleteFunctionInput{
- FunctionName: aws.String(function_name),
- })
- if err != nil {
- return fmt.Errorf("failed to delete Lambda function: %v", err)
- }
- return nil
- }
|