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

feat(apim): crds and controller for configuring apis in apim #1175

Merged
merged 16 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 15 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
2 changes: 2 additions & 0 deletions services/dis-apim-operator/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ go.work
*.swp
*.swo
*~

*.ignore.*
93 changes: 89 additions & 4 deletions services/dis-apim-operator/api/v1alpha1/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ limitations under the License.
package v1alpha1

import (
"fmt"

"github.com/Altinn/altinn-platform/services/dis-apim-operator/internal/utils"
apim "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -28,18 +32,52 @@ type ApiSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Foo is an example field of Api. Edit api_types.go to remove/update
Foo string `json:"foo,omitempty"`
// DisplayName - The display name of the API. This name is used by the developer portal as the API name.
// +kubebuilder:validation:Required
DisplayName string `json:"displayName"`
// Description - Description of the API. May include its purpose, where to get more information, and other relevant information.
// +kubebuilder:validation:Optional
Description *string `json:"description,omitempty"`
// VersioningScheme - Indicates the versioning scheme used for the API. Possible values include, but are not limited to, "Segment", "Query", "Header". Default value is "Segment".
// +kubebuilder:validation:Optional
// +kubebuilder:default:="Segment"
// +kubebuilder:validation:Enum:=Header;Query;Segment
VersioningScheme APIVersionScheme `json:"versioningScheme,omitempty"`
// Path - API prefix. The value is combined with the API version to form the URL of the API endpoint.
// +kubebuilder:validation:Required
Path string `json:"path"`
// ApiType - Type of API.
// +kubebuilder:validation:Optional
// +kubebuilder:default:="http"
// +default:value:"http"
// +kubebuilder:validation:Enum:=graphql;http;websocket
ApiType *APIType `json:"apiType,omitempty"`
// Contact - Contact details of the API owner.
// +kubebuilder:validation:Optional
Contact *APIContactInformation `json:"contact,omitempty"`
// Versions - A list of API versions associated with the API. If the API is specified using the OpenAPI definition, then the API version is set by the version field of the OpenAPI definition.
// +kubebuilder:validation:Required
Versions []ApiVersionSubSpec `json:"versions"`
}

// ApiStatus defines the observed state of Api.
type ApiStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// ProvisioningState - The provisioning state of the API. Possible values are: Succeeded, Failed, Updating, Deleting.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:enum:=Succeeded;Failed;Updating;Deleting
ProvisioningState ProvisioningState `json:"provisioningState,omitempty"`
// ApiVersionSetID - The identifier of the API Version Set.
// +kubebuilder:validation:Optional
ApiVersionSetID string `json:"apiVersionSetID,omitempty"`
// VersionStates - A list of API Version deployed in the API Management service and the current state of the API Version.
// +kubebuilder:validation:Optional
VersionStates map[string]ApiVersionStatus `json:"versionStates,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.provisioningState`
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// Api is the Schema for the apis API.
type Api struct {
Expand All @@ -62,3 +100,50 @@ type ApiList struct {
func init() {
SchemeBuilder.Register(&Api{}, &ApiList{})
}

// GetApiAzureFullName returns the name of the Azure resource.
func (a *Api) GetApiAzureFullName() string {
if a == nil {
return ""
}
return fmt.Sprintf("%s-%s", a.Namespace, a.Name)
}

// ToAzureApiVersionSet returns an APIVersionSetContract object.
func (a *Api) ToAzureApiVersionSet() apim.APIVersionSetContract {
if a == nil {
return apim.APIVersionSetContract{}
}
return apim.APIVersionSetContract{
Properties: &apim.APIVersionSetContractProperties{
DisplayName: &a.Spec.DisplayName,
VersioningScheme: a.Spec.VersioningScheme.AzureAPIVersionScheme(),
Description: a.Spec.Description,
},
Name: utils.ToPointer(a.GetApiAzureFullName()),
}
}

// ToApiVersions returns a map of ApiVersion type.
func (a *Api) ToApiVersions() map[string]ApiVersion {
apiVersions := make(map[string]ApiVersion)
for _, version := range a.Spec.Versions {
versionFullName := version.GetApiVersionFullName(a.Name)
apiVersion := ApiVersion{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: versionFullName,
Namespace: a.Namespace,
},
Spec: ApiVersionSpec{
ApiVersionSetId: a.Status.ApiVersionSetID,
ApiVersionScheme: a.Spec.VersioningScheme,
Path: a.Spec.Path,
ApiType: a.Spec.ApiType,
ApiVersionSubSpec: version,
},
}
apiVersions[version.GetApiVersionSpecifier()] = apiVersion
}
return apiVersions
}
157 changes: 157 additions & 0 deletions services/dis-apim-operator/api/v1alpha1/apiversion_enums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package v1alpha1

import (
"github.com/Altinn/altinn-platform/services/dis-apim-operator/internal/utils"
apim "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
)

// INSERT ADDITIONAL TYPES
// Important: Run "make" to regenerate code after modifying this file

// ContentFormat - Format of the Content in which the API is getting imported.
type ContentFormat string

const (
// ContentFormatGraphqlLink - The GraphQL API endpoint hosted on a publicly accessible internet address.
ContentFormatGraphqlLink ContentFormat = "graphql-link"
// ContentFormatOpenapi - The contents are inline and Content Type is a OpenAPI 3.0 YAML Document.
ContentFormatOpenapi ContentFormat = "openapi"
// ContentFormatOpenapiJSON - The contents are inline and Content Type is a OpenAPI 3.0 JSON Document.
ContentFormatOpenapiJSON ContentFormat = "openapi+json"
// ContentFormatOpenapiJSONLink - The OpenAPI 3.0 JSON document is hosted on a publicly accessible internet address.
ContentFormatOpenapiJSONLink ContentFormat = "openapi+json-link"
// ContentFormatOpenapiLink - The OpenAPI 3.0 YAML document is hosted on a publicly accessible internet address.
ContentFormatOpenapiLink ContentFormat = "openapi-link"
// ContentFormatSwaggerJSON - The contents are inline and Content Type is a OpenAPI 2.0 JSON Document.
ContentFormatSwaggerJSON ContentFormat = "swagger-json"
// ContentFormatSwaggerLinkJSON - The OpenAPI 2.0 JSON document is hosted on a publicly accessible internet address.
ContentFormatSwaggerLinkJSON ContentFormat = "swagger-link-json"
// ContentFormatWadlLinkJSON - The WADL document is hosted on a publicly accessible internet address.
ContentFormatWadlLinkJSON ContentFormat = "wadl-link-json"
// ContentFormatWadlXML - The contents are inline and Content type is a WADL document.
ContentFormatWadlXML ContentFormat = "wadl-xml"
)

func (c ContentFormat) AzureContentFormat() *apim.ContentFormat {
contentFormat := apim.ContentFormat(c)
return &contentFormat
}

type APIContactInformation struct {
// The email address of the contact person/organization. MUST be in the format of an email address
Email *string `json:"email,omitempty"`

// The identifying name of the contact person/organization
Name *string `json:"name,omitempty"`

// The URL pointing to the contact information. MUST be in the format of a URL
URL *string `json:"url,omitempty"`
}

func (a *APIContactInformation) AzureAPIContactInformation() *apim.APIContactInformation {
if a == nil {
return nil
}
return &apim.APIContactInformation{
Email: a.Email,
Name: a.Name,
URL: a.URL,
}
}

type APIVersionScheme string

const (
// APIVersionSetContractDetailsVersioningSchemeHeader - The API Version is passed in a HTTP header.
APIVersionSetContractDetailsVersioningSchemeHeader APIVersionScheme = "Header"
// APIVersionSetContractDetailsVersioningSchemeQuery - The API Version is passed in a query parameter.
APIVersionSetContractDetailsVersioningSchemeQuery APIVersionScheme = "Query"
// APIVersionSetContractDetailsVersioningSchemeSegment - The API Version is passed in a path segment.
APIVersionSetContractDetailsVersioningSchemeSegment APIVersionScheme = "Segment"
)

func (a *APIVersionScheme) AzureAPIVersionScheme() *apim.VersioningScheme {
if a == nil {
return nil
}
apiVersionScheme := apim.VersioningScheme(*a)
return &apiVersionScheme
}

func (a *APIVersionScheme) AzureAPIVersionSetContractDetailsVersioningScheme() *apim.APIVersionSetContractDetailsVersioningScheme {
if a == nil {
return nil
}
apiVersionScheme := apim.APIVersionSetContractDetailsVersioningScheme(*a)
return &apiVersionScheme
}

type Protocol string

const (
ProtocolHTTP Protocol = "http"
ProtocolHTTPS Protocol = "https"
ProtocolWs Protocol = "ws"
ProtocolWss Protocol = "wss"
)

func (p *Protocol) AzureProtocol() *apim.Protocol {
if p == nil {
return nil
}
protocol := apim.Protocol(*p)
return &protocol
}

func ToApimProtocolSlice(protocols []Protocol) []*apim.Protocol {
apimProtocols := make([]*apim.Protocol, len(protocols))
for i, protocol := range protocols {
apimProtocols[i] = utils.ToPointer(apim.Protocol(protocol))
}
return apimProtocols
}

type PolicyFormat string

const (
// PolicyContentFormatRawxml - The contents are inline and Content type is a non XML encoded policy document.
PolicyContentFormatRawxml PolicyFormat = "rawxml"
// PolicyContentFormatRawxmlLink - The policy document is not XML encoded and is hosted on a HTTP endpoint accessible from
// the API Management service.
PolicyContentFormatRawxmlLink PolicyFormat = "rawxml-link"
// PolicyContentFormatXML - The contents are inline and Content type is an XML document.
PolicyContentFormatXML PolicyFormat = "xml"
// PolicyContentFormatXMLLink - The policy XML document is hosted on a HTTP endpoint accessible from the API Management service.
PolicyContentFormatXMLLink PolicyFormat = "xml-link"
)

func (p *PolicyFormat) AzurePolicyFormat() *apim.PolicyContentFormat {
if p == nil {
return nil
}
policyFormat := apim.PolicyContentFormat(*p)
return &policyFormat
}

// APIType - Type of API.
type APIType string

const (
APITypeGraphql APIType = "graphql"
APITypeHTTP APIType = "http"
APITypeWebsocket APIType = "websocket"
)

func (a APIType) AzureApiType() *apim.APIType {
apiType := apim.APIType(a)
return &apiType
}

type ProvisioningState string

const (
ProvisioningStateSucceeded ProvisioningState = "Succeeded"
ProvisioningStateFailed ProvisioningState = "Failed"
ProvisioningStateUpdating ProvisioningState = "Updating"
ProvisioningStateDeleting ProvisioningState = "Deleting"
)
Loading
Loading