mz_cloud_resources/crd/
vpc_endpoint.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! VpcEndpoint custom resource, to be reconciled into an AWS VPC Endpoint by the
11//! environment-controller.
12use std::fmt;
13
14use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition;
15use kube::CustomResource;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19pub mod v1 {
20    use super::*;
21
22    /// Describes an AWS VPC endpoint to create.
23    #[derive(CustomResource, Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
24    #[serde(rename_all = "camelCase")]
25    #[kube(
26        group = "materialize.cloud",
27        version = "v1",
28        kind = "VpcEndpoint",
29        singular = "vpcendpoint",
30        plural = "vpcendpoints",
31        shortname = "vpce",
32        namespaced,
33        status = "VpcEndpointStatus",
34        printcolumn = r#"{"name": "AwsServiceName", "type": "string", "description": "Name of the VPC Endpoint Service to connect to.", "jsonPath": ".spec.awsServiceName", "priority": 1}"#,
35        printcolumn = r#"{"name": "AvailabilityZoneIDs", "type": "string", "description": "Availability Zone IDs", "jsonPath": ".spec.availabilityZoneIds", "priority": 1}"#
36    )]
37    // If making changes to this spec,
38    // you must also update src/cloud-resources/gen/vpcendpoints.crd.json
39    // so that cloudtest can register the CRD.
40    pub struct VpcEndpointSpec {
41        /// The name of the service to connect to.
42        pub aws_service_name: String,
43        /// The IDs of the availability zones in which the service is available.
44        pub availability_zone_ids: Vec<String>,
45        /// A suffix to use in the name of the IAM role that is created.
46        pub role_suffix: String,
47    }
48
49    #[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
50    #[serde(rename_all = "camelCase")]
51    pub struct VpcEndpointStatus {
52        // This will be None if the customer hasn't allowed our principal, got the name of their
53        // VPC Endpoint Service wrong, or we've otherwise failed to create the VPC Endpoint.
54        pub vpc_endpoint_id: Option<String>,
55        pub state: Option<VpcEndpointState>,
56        pub conditions: Option<Vec<Condition>>,
57    }
58
59    #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
60    #[serde(rename_all = "camelCase")]
61    pub enum VpcEndpointState {
62        // Internal States
63        PendingServiceDiscovery,
64        CreatingEndpoint,
65        RecreatingEndpoint,
66        UpdatingEndpoint,
67
68        // AWS States
69        // Connection established to the customer's VPC Endpoint Service.
70        Available,
71        Deleted,
72        Deleting,
73        Expired,
74        Failed,
75        // Customer has approved the connection. It should eventually move to Available.
76        Pending,
77        // Waiting on the customer to approve the connection.
78        PendingAcceptance,
79        Rejected,
80        Unknown,
81    }
82
83    impl fmt::Display for VpcEndpointState {
84        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85            let repr = match self {
86                VpcEndpointState::PendingServiceDiscovery => "pending-service-discovery",
87                VpcEndpointState::CreatingEndpoint => "creating-endpoint",
88                VpcEndpointState::RecreatingEndpoint => "recreating-endpoint",
89                VpcEndpointState::UpdatingEndpoint => "updating-endpoint",
90                VpcEndpointState::Available => "available",
91                VpcEndpointState::Deleted => "deleted",
92                VpcEndpointState::Deleting => "deleting",
93                VpcEndpointState::Expired => "expired",
94                VpcEndpointState::Failed => "failed",
95                VpcEndpointState::Pending => "pending",
96                VpcEndpointState::PendingAcceptance => "pending-acceptance",
97                VpcEndpointState::Rejected => "rejected",
98                VpcEndpointState::Unknown => "unknown",
99            };
100            write!(f, "{}", repr)
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use std::fs;
108
109    use kube::CustomResourceExt;
110    use kube::core::crd::merge_crds;
111
112    #[mz_ore::test]
113    fn test_vpc_endpoint_crd_matches() {
114        let crd = merge_crds(vec![super::v1::VpcEndpoint::crd()], "v1").unwrap();
115        let crd_json = serde_json::to_string(&serde_json::json!(&crd)).unwrap();
116        let exported_crd_json = fs::read_to_string("src/crd/generated/vpcendpoints.json").unwrap();
117        let exported_crd_json = exported_crd_json.trim();
118        assert_eq!(
119            &crd_json, exported_crd_json,
120            "VpcEndpoint CRD json does not match exported json.\n\nCRD:\n{}\n\nExported CRD:\n{}",
121            &crd_json, exported_crd_json,
122        );
123    }
124}