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

Manage DNS records #4487

Merged
merged 7 commits into from
Mar 18, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Update graphql schema, setup cynic based on the new schema
  • Loading branch information
ayys authored and theduke committed Mar 18, 2024
commit 85c5a040151eca86135f5278abc7422c93a1c9cd
100 changes: 71 additions & 29 deletions lib/backend-api/schema.graphql
Original file line number Diff line number Diff line change
@@ -1511,6 +1511,10 @@ type DNSDomain implements Node {

"""This zone will be accessible at /dns/{slug}/."""
slug: String!
zoneFile: String!
createdAt: DateTime!
updatedAt: DateTime!
deletedAt: DateTime

"""The ID of the object"""
id: ID!
@@ -1528,16 +1532,18 @@ type ARecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

interface DNSRecordInterface {
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
createdAt: DateTime!
updatedAt: DateTime!
@@ -1552,9 +1558,10 @@ type AAAARecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1568,9 +1575,10 @@ type CNAMERecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1582,9 +1590,10 @@ type TXTRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1597,9 +1606,10 @@ type MXRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1611,9 +1621,10 @@ type NSRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1627,9 +1638,10 @@ type CAARecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1656,9 +1668,10 @@ type DNAMERecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1670,9 +1683,10 @@ type PTRRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1712,9 +1726,10 @@ type SOARecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1747,9 +1762,10 @@ type SRVRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1763,9 +1779,10 @@ type SSHFPRecord implements Node & DNSRecordInterface {

"""The ID of the object"""
id: ID!
name: String
ttl: Int
name: String!
ttl: Int!
dnsClass: String
text: String!
domain: DNSDomain!
}

@@ -1904,7 +1921,7 @@ type UserNotificationKindValidateEmail {

Enum of ways a user can login. One user can have many login methods
associated with their account.

"""
enum LoginMethod {
GOOGLE
@@ -2177,10 +2194,15 @@ input WorkloadRunnerWasmSourceV1 {
type Query {
latestTOS: TermsOfService!
getDeployAppVersion(name: String!, owner: String, version: String): DeployAppVersion
getAllDomains(offset: Int, before: String, after: String, first: Int, last: Int): DNSDomainConnection!
getAllDomains(namespace: String, offset: Int, before: String, after: String, first: Int, last: Int): DNSDomainConnection!
getAllDNSRecords(sortBy: DNSRecordsSortBy, updatedAfter: DateTime, before: String, after: String, first: Int, last: Int): DNSRecordConnection!
getDomain(name: String!): DNSDomain
getDeployApp(name: String!, owner: String): DeployApp
getDeployApp(
name: String!

"""Owner of the app. Defaults to logged in user."""
owner: String
): DeployApp
getAppByGlobalAlias(alias: String!): DeployApp
getDeployApps(sortBy: DeployAppsSortBy, updatedAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppConnection!
getAppVersions(sortBy: DeployAppVersionsSortBy, updatedAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppVersionConnection!
@@ -2728,6 +2750,7 @@ type Mutation {
registerDomain(input: RegisterDomainInput!): RegisterDomainPayload
upsertDNSRecord(input: UpsertDNSRecordInput!): UpsertDNSRecordPayload
deleteDnsRecord(input: DeleteDNSRecordInput!): DeleteDNSRecordPayload
upsertDomainFromZoneFile(input: UpsertDomainFromZoneFileInput!): UpsertDomainFromZoneFilePayload
deleteDomain(input: DeleteDomainInput!): DeleteDomainPayload
tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload
generateDeployToken(input: GenerateDeployTokenInput!): GenerateDeployTokenPayload
@@ -3028,11 +3051,13 @@ type RegisterDomainPayload {
input RegisterDomainInput {
name: String!
namespace: String!
importRecords: Boolean = false
clientMutationId: String
}

type UpsertDNSRecordPayload {
success: Boolean!
record: DNSRecord!
clientMutationId: String
}

@@ -3055,6 +3080,11 @@ enum RecordKind {
NS
TXT
DNAME
PTR
SOA
SRV
CAA
SSHFP
}

input DNSMXExtraInput {
@@ -3071,6 +3101,18 @@ input DeleteDNSRecordInput {
clientMutationId: String
}

type UpsertDomainFromZoneFilePayload {
success: Boolean!
domain: DNSDomain!
clientMutationId: String
}

input UpsertDomainFromZoneFileInput {
zoneFile: String!
deleteMissingRecords: Boolean
clientMutationId: String
}

type DeleteDomainPayload {
success: Boolean!
clientMutationId: String
76 changes: 75 additions & 1 deletion lib/backend-api/src/query.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ use crate::{
self, CreateNamespaceVars, DeployApp, DeployAppConnection, DeployAppVersion,
DeployAppVersionConnection, GetCurrentUserWithAppsVars, GetDeployAppAndVersion,
GetDeployAppVersionsVars, GetNamespaceAppsVars, Log, LogStream, PackageVersionConnection,
PublishDeployAppVars,
PublishDeployAppVars, UpsertDomainFromZoneFileVars, DnsDomain,
},
GraphQLApiFailure, WasmerClient,
};
@@ -822,6 +822,39 @@ pub async fn get_app_logs_paginated(
pub async fn get_domain(
client: &WasmerClient,
domain: String,
) -> Result<Option<types::DnsDomain>, anyhow::Error> {
let vars = types::GetDomainVars { domain };

let opt = client
.run_graphql(types::GetDomain::build(vars))
.await
.map_err(anyhow::Error::from)?
.get_domain;
Ok(opt)
}

/// Retrieve a domain by its name.
///
/// Specify with_records to also retrieve all records for the domain.
pub async fn get_domain_zone_file(
client: &WasmerClient,
domain: String,
) -> Result<Option<types::DnsDomainWithZoneFile>, anyhow::Error> {
let vars = types::GetDomainVars { domain };

let opt = client
.run_graphql(types::GetDomainWithZoneFile::build(vars))
.await
.map_err(anyhow::Error::from)?
.get_domain;
Ok(opt)
}


/// Retrieve a domain by its name, along with all it's records.
pub async fn get_domain_with_records(
client: &WasmerClient,
domain: String,
) -> Result<Option<types::DnsDomainWithRecords>, anyhow::Error> {
let vars = types::GetDomainVars { domain };

@@ -847,6 +880,19 @@ pub async fn get_all_dns_records(
.map(|x| x.get_all_dnsrecords)
}

/// Retrieve all DNS domains.
pub async fn get_all_domains(
client: &WasmerClient,
vars: types::GetAllDomainsVariables,
) -> Result<types::DnsDomainConnection, anyhow::Error> {
client
.run_graphql_strict(types::GetAllDomains::build(vars))
.await
.map_err(anyhow::Error::from)
.map(|x| x.get_all_domains)
}


/// Retrieve a domain by its name.
///
/// Specify with_records to also retrieve all records for the domain.
@@ -892,3 +938,31 @@ fn unix_timestamp(ts: OffsetDateTime) -> f64 {

(secs as f64) + (nanos as f64 / nanos_per_second as f64)
}

/// Publish a new app (version).
pub async fn upsert_domain_from_zone_file(
client: &WasmerClient,
zone_file_contents: String,
delete_missing_records: bool,
) -> Result<DnsDomain, anyhow::Error> {
let vars = UpsertDomainFromZoneFileVars {
zone_file: zone_file_contents,
delete_missing_records: Some(delete_missing_records),
};
let res = client
.run_graphql_raw(types::UpsertDomainFromZoneFile::build(vars))
theduke marked this conversation as resolved.
Show resolved Hide resolved
.await?;

if let Some(domain) = res
.data
.and_then(|d| d.upsert_domain_from_zone_file)
.map(|d| d.domain)
{
Ok(domain)
} else {
Err(GraphQLApiFailure::from_errors(
"could not sync zone file",
res.errors,
))
}
}
Loading