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

Adding AI provider selection and backend API Key in APIM API to APK Conf implementation #1190

Merged
merged 1 commit into from
Sep 24, 2024
Merged
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
8 changes: 4 additions & 4 deletions apim-apk-agent/internal/k8sClient/k8s_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ func DeployBackendJWTCR(backendJWT *dpv1alpha1.BackendJWT, k8sClient client.Clie
}

// DeployAPIPolicyCR applies the given APIPolicies struct to the Kubernetes cluster.
func DeployAPIPolicyCR(apiPolicies *dpv1alpha2.APIPolicy, k8sClient client.Client) {
crAPIPolicies := &dpv1alpha2.APIPolicy{}
func DeployAPIPolicyCR(apiPolicies *dpv1alpha3.APIPolicy, k8sClient client.Client) {
crAPIPolicies := &dpv1alpha3.APIPolicy{}
if err := k8sClient.Get(context.Background(), client.ObjectKey{Namespace: apiPolicies.ObjectMeta.Namespace, Name: apiPolicies.Name}, crAPIPolicies); err != nil {
if !k8error.IsNotFound(err) {
loggers.LoggerK8sClient.Error("Unable to get APIPolicies CR: " + err.Error())
Expand Down Expand Up @@ -398,8 +398,8 @@ func UpdateRateLimitPolicyCR(policy eventhubTypes.RateLimitPolicy, k8sClient cli
}

// DeployBackendCR applies the given Backends struct to the Kubernetes cluster.
func DeployBackendCR(backends *dpv1alpha1.Backend, k8sClient client.Client) {
crBackends := &dpv1alpha1.Backend{}
func DeployBackendCR(backends *dpv1alpha2.Backend, k8sClient client.Client) {
crBackends := &dpv1alpha2.Backend{}
if err := k8sClient.Get(context.Background(), client.ObjectKey{Namespace: backends.ObjectMeta.Namespace, Name: backends.Name}, crBackends); err != nil {
if !k8error.IsNotFound(err) {
loggers.LoggerK8sClient.Error("Unable to get Backends CR: " + err.Error())
Expand Down
3 changes: 2 additions & 1 deletion apim-apk-agent/pkg/eventhub/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ type AIProvider struct {

// Config for struct Metadata
type Config struct {
Metadata []Fields `json:"metadata"`
Metadata []Fields `json:"metadata"`
AuthHeader string `json:"authHeader"`
}

// Fields for struct Fields
Expand Down
9 changes: 9 additions & 0 deletions apim-apk-agent/pkg/transformer/api_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type CustomParams struct {
type SecurityObj struct {
Enabled bool `json:"enabled"`
Type string `json:"type"`
APIKeyValue string `json:"apiKeyValue"`
APIKeyIdentifier string `json:"apiKeyIdentifier"`
Username string `json:"username"`
Password string `json:"password"`
GrantType string `json:"grantType"`
Expand Down Expand Up @@ -176,6 +178,13 @@ type APIMApi struct {
RevisionedAPIID string `yaml:"revisionedApiId"`
APIThrottlingPolicy string `yaml:"apiThrottlingPolicy"`
APIPolicies APIMOperationPolicies `yaml:"apiPolicies"`
AIConfiguration APIMAIConfiguration `yaml:"aiConfiguration"`
}

// APIMAIConfiguration holds the configuration details for AI providers
type APIMAIConfiguration struct {
LLMProviderName string `yaml:"llmProviderName"`
LLMProviderAPIVersion string `yaml:"llmProviderApiVersion"`
}

// APIYaml is a wrapper struct for YAML representation of an API.
Expand Down
16 changes: 13 additions & 3 deletions apim-apk-agent/pkg/transformer/apk_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ package transformer

// SecretInfo holds the info related to the created secret upon enabling the endpoint security options like basic auth
type SecretInfo struct {
SecretName string `yaml:"secretName,omitempty"`
UsernameKey string `yaml:"userNameKey,omitempty"`
PasswordKey string `yaml:"passwordKey,omitempty"`
SecretName string `yaml:"secretName,omitempty"`
UsernameKey string `yaml:"userNameKey,omitempty"`
PasswordKey string `yaml:"passwordKey,omitempty"`
In string `yaml:"in,omitempty"`
APIKeyNameKey string `yaml:"apiKeyNameKey,omitempty"`
APIKeyValueKey string `yaml:"apiKeyValueKey,omitempty"`
}

// EndpointSecurity comtains the information related to endpoint security configurations enabled by a user for a given API
Expand Down Expand Up @@ -108,6 +111,12 @@ type VHost struct {
Sandbox []string `yaml:"sandbox,omitempty"`
}

// AIProvider represents the AI provider configuration.
type AIProvider struct {
Name string `yaml:"name,omitempty"`
APIVersion string `yaml:"apiVersion,omitempty"`
}

// API represents an main API type definition
type API struct {
Name string `yaml:"name,omitempty"`
Expand All @@ -125,4 +134,5 @@ type API struct {
SubscriptionValidation bool `yaml:"subscriptionValidation,omitempty"`
RateLimit *RateLimit `yaml:"rateLimit,omitempty"`
APIPolicies *OperationPolicies `yaml:"apiPolicies,omitempty"`
AIProvider *AIProvider `yaml:"aiProvider,omitempty"`
}
5 changes: 3 additions & 2 deletions apim-apk-agent/pkg/transformer/k8s_artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package transformer
import (
"github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1"
dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2"
dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3"
corev1 "k8s.io/api/core/v1"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
)
Expand All @@ -12,10 +13,10 @@ type K8sArtifacts struct {
API dpv1alpha2.API
HTTPRoutes map[string]*gwapiv1.HTTPRoute
GQLRoutes map[string]*dpv1alpha2.GQLRoute
Backends map[string]*v1alpha1.Backend
Backends map[string]*dpv1alpha2.Backend
Scopes map[string]*v1alpha1.Scope
Authentication map[string]*dpv1alpha2.Authentication
APIPolicies map[string]*dpv1alpha2.APIPolicy
APIPolicies map[string]*dpv1alpha3.APIPolicy
InterceptorServices map[string]*v1alpha1.InterceptorService
ConfigMaps map[string]*corev1.ConfigMap
Secrets map[string]*corev1.Secret
Expand Down
103 changes: 75 additions & 28 deletions apim-apk-agent/pkg/transformer/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (

dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1"
dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2"
dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
Expand Down Expand Up @@ -83,6 +84,14 @@ func GenerateAPKConf(APIJson string, certArtifact CertificateArtifact, organizat
apk.DefinitionPath = "/definition"
apk.SubscriptionValidation = true

if apiYamlData.AIConfiguration.LLMProviderName != "" && apiYamlData.AIConfiguration.LLMProviderAPIVersion != "" {
sha1ValueforCRName := GetSha1Value(apiYamlData.AIConfiguration.LLMProviderName + "-" + apiYamlData.AIConfiguration.LLMProviderAPIVersion + "-" + organizationID)
apk.AIProvider = &AIProvider{
Name: sha1ValueforCRName,
APIVersion: apiYamlData.AIConfiguration.LLMProviderAPIVersion,
}
}

if apiYamlData.APIThrottlingPolicy != "" {
rateLimitPolicy := managementserver.GetRateLimitPolicy(apiYamlData.APIThrottlingPolicy, organizationID)
logger.LoggerTransformer.Debugf("Rate Limit Policy: %v", rateLimitPolicy)
Expand Down Expand Up @@ -584,19 +593,37 @@ func getEndpointConfigs(sandboxURL string, prodURL string, endCertAvailable bool

if endpointSecurityData.Sandbox.Enabled {
sandboxEndpointConf.EndSecurity.Enabled = true
sandboxEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "sandbox", "secret"}, "-"),
UsernameKey: "username",
PasswordKey: "password",
if endpointSecurityData.Sandbox.Type == "apikey" {
sandboxEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "sandbox", "secret"}, "-"),
In: "Header",
APIKeyNameKey: endpointSecurityData.Sandbox.APIKeyIdentifier,
APIKeyValueKey: "apiKey",
}
} else {
sandboxEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "sandbox", "secret"}, "-"),
UsernameKey: "username",
PasswordKey: "password",
}
}
}

if endpointSecurityData.Production.Enabled {
prodEndpointConf.EndSecurity.Enabled = true
prodEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "production", "secret"}, "-"),
UsernameKey: "username",
PasswordKey: "password",
if endpointSecurityData.Production.Type == "apikey" {
prodEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "production", "secret"}, "-"),
In: "Header",
APIKeyNameKey: endpointSecurityData.Production.APIKeyIdentifier,
APIKeyValueKey: "apiKey",
}
} else {
prodEndpointConf.EndSecurity.SecurityType = SecretInfo{
SecretName: strings.Join([]string{apiUniqueID, "production", "secret"}, "-"),
UsernameKey: "username",
PasswordKey: "password",
}
}
}

Expand All @@ -621,7 +648,7 @@ func getEndpointConfigs(sandboxURL string, prodURL string, endCertAvailable bool
// GenerateCRs takes the .apk-conf, api definition, vHost and the organization for a particular API and then generate and returns
// the relavant CRD set as a zip
func GenerateCRs(apkConf string, apiDefinition string, certContainer CertContainer, k8ResourceGenEndpoint string, organizationID string) (*K8sArtifacts, error) {
k8sArtifact := K8sArtifacts{HTTPRoutes: make(map[string]*gwapiv1.HTTPRoute), GQLRoutes: make(map[string]*dpv1alpha2.GQLRoute), Backends: make(map[string]*dpv1alpha1.Backend), Scopes: make(map[string]*dpv1alpha1.Scope), Authentication: make(map[string]*dpv1alpha2.Authentication), APIPolicies: make(map[string]*dpv1alpha2.APIPolicy), InterceptorServices: make(map[string]*dpv1alpha1.InterceptorService), ConfigMaps: make(map[string]*corev1.ConfigMap), Secrets: make(map[string]*corev1.Secret), RateLimitPolicies: make(map[string]*dpv1alpha1.RateLimitPolicy)}
k8sArtifact := K8sArtifacts{HTTPRoutes: make(map[string]*gwapiv1.HTTPRoute), GQLRoutes: make(map[string]*dpv1alpha2.GQLRoute), Backends: make(map[string]*dpv1alpha2.Backend), Scopes: make(map[string]*dpv1alpha1.Scope), Authentication: make(map[string]*dpv1alpha2.Authentication), APIPolicies: make(map[string]*dpv1alpha3.APIPolicy), InterceptorServices: make(map[string]*dpv1alpha1.InterceptorService), ConfigMaps: make(map[string]*corev1.ConfigMap), Secrets: make(map[string]*corev1.Secret), RateLimitPolicies: make(map[string]*dpv1alpha1.RateLimitPolicy)}
if apkConf == "" {
logger.LoggerTransformer.Error("Empty apk-conf parameter provided. Unable to generate CRDs.")
return nil, errors.New("Error: APK-Conf can't be empty")
Expand Down Expand Up @@ -724,7 +751,7 @@ func GenerateCRs(apkConf string, apiDefinition string, certContainer CertContain

switch kind {
case "APIPolicy":
var apiPolicy dpv1alpha2.APIPolicy
var apiPolicy dpv1alpha3.APIPolicy
err = k8Yaml.Unmarshal(yamlData, &apiPolicy)
if err != nil {
logger.LoggerSync.Errorf("Error unmarshaling APIPolicy YAML: %v", err)
Expand All @@ -741,7 +768,7 @@ func GenerateCRs(apkConf string, apiDefinition string, certContainer CertContain
k8sArtifact.HTTPRoutes[httpRoute.ObjectMeta.Name] = &httpRoute

case "Backend":
var backend dpv1alpha1.Backend
var backend dpv1alpha2.Backend
err = k8Yaml.Unmarshal(yamlData, &backend)
if err != nil {
logger.LoggerSync.Errorf("Error unmarshaling Backend YAML: %v", err)
Expand Down Expand Up @@ -1034,31 +1061,51 @@ func createConfigMaps(certFiles map[string]string, k8sArtifact *K8sArtifacts) {

// createEndpointSecrets creates and links the secret CRs need to be created for handling the endpoint security
func createEndpointSecrets(secretData EndpointSecurityConfig, k8sArtifact *K8sArtifacts) {
createSecret := func(environment string, username, password string) {
secret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: strings.Join([]string{k8sArtifact.API.Name, environment, "secret"}, "-"),
Namespace: k8sArtifact.API.Namespace,
Labels: make(map[string]string),
},
Data: map[string][]byte{
"username": []byte(username),
"password": []byte(password),
},
createSecret := func(environment string, username, password string, apiKeyValue string, securityType string) {
var secret corev1.Secret
if securityType == "apikey" {
secret = corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: strings.Join([]string{k8sArtifact.API.Name, environment, "secret"}, "-"),
Namespace: k8sArtifact.API.Namespace,
Labels: make(map[string]string),
},
Data: map[string][]byte{
"apiKey": []byte(apiKeyValue),
},
}
} else {
secret = corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: strings.Join([]string{k8sArtifact.API.Name, environment, "secret"}, "-"),
Namespace: k8sArtifact.API.Namespace,
Labels: make(map[string]string),
},
Data: map[string][]byte{
"username": []byte(username),
"password": []byte(password),
},
}
}
logger.LoggerTransformer.Debugf("New Secret Data for %s: %v", environment, secret)
k8sArtifact.Secrets[secret.ObjectMeta.Name] = &secret
}

if secretData.Production.Enabled {
createSecret("production", secretData.Production.Username, secretData.Production.Password)
if secretData.Production.Username == "" || secretData.Production.Password == "" {
createSecret("production", secretData.Production.Username, secretData.Production.Password, secretData.Production.APIKeyValue, secretData.Production.Type)
}
}

if secretData.Sandbox.Enabled {
createSecret("sandbox", secretData.Sandbox.Username, secretData.Sandbox.Password)
createSecret("sandbox", secretData.Sandbox.Username, secretData.Sandbox.Password, secretData.Sandbox.APIKeyValue, secretData.Sandbox.Type)
}
}
8 changes: 8 additions & 0 deletions apim-apk-agent/pkg/transformer/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,11 @@ func GetUniqueIDForAPI(name, version, organization string) string {
hashedValue := hash.Sum(nil)
return hex.EncodeToString(hashedValue)
}

// GetSha1Value returns the SHA1 value of the input string
func GetSha1Value(input string) string {
hasher := sha1.New()
hasher.Write([]byte(input))
hashBytes := hasher.Sum(nil)
return hex.EncodeToString(hashBytes)
}
Loading