You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2023/08/24 18:16:39 UTC

[trafficcontrol] branch master updated: Api v5 should return rfc3339 timestamps for static dns entries (#7740)

This is an automated email from the ASF dual-hosted git repository.

shamrick pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new c1f82c4000 Api v5 should return rfc3339 timestamps for static dns entries (#7740)
c1f82c4000 is described below

commit c1f82c4000e28a101756e80a260bac85ca7a93c8
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Thu Aug 24 12:16:32 2023 -0600

    Api v5 should return rfc3339 timestamps for static dns entries (#7740)
    
    * wip
    
    * tests passing
    
    * Adding changelog and docs
    
    * cleanup
    
    * remove unneeded commit
---
 CHANGELOG.md                                       |   2 +-
 docs/source/api/v5/staticdnsentries.rst            |  12 +-
 lib/go-tc/staticdnsentry.go                        |  70 ++++
 traffic_ops/testing/api/v5/cdn_locks_test.go       |   8 +-
 .../testing/api/v5/staticdnsentries_test.go        | 117 +++----
 traffic_ops/testing/api/v5/traffic_control_test.go |   2 +-
 traffic_ops/traffic_ops_golang/routing/routes.go   |   8 +-
 .../staticdnsentry/staticdnsentry.go               | 382 +++++++++++++++++++++
 traffic_ops/v5-client/staticdnsentry.go            |  42 +--
 9 files changed, 550 insertions(+), 93 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a46e6c808..783e8a05e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -157,7 +157,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - [#7698](https://github.com/apache/trafficcontrol/pull/7698) *Traffic Ops* Fixes `region` v5 apis to respond with `RFC3339` date/time Format.
 - [#7686](https://github.com/apache/trafficcontrol/pull/7686) *Traffic Ops* Fixes secured parameters being visible when role has proper permissions.
 - [#7697](https://github.com/apache/trafficcontrol/pull/7697) *Traffic Ops* Fixes `iloPassword` and `xmppPassword` checking for priv-level instead of using permissions.
-
+- [#7740](https://github.com/apache/trafficcontrol/pull/7740) *Traffic Ops* Fixes `staticDNSEntries` v5 apis to respond with `RFC3339` date/time Format.
 ### Removed
 - [#7271](https://github.com/apache/trafficcontrol/pull/7271) Remove components in `infrastructre/docker/`, not in use as cdn-in-a-box performs the same functionality.
 - [#7271](https://github.com/apache/trafficcontrol/pull/7271) Remove`misc/jira_github_issue_import.py`, the project does not use JIRA.
diff --git a/docs/source/api/v5/staticdnsentries.rst b/docs/source/api/v5/staticdnsentries.rst
index fb92ee4687..346e183017 100644
--- a/docs/source/api/v5/staticdnsentries.rst
+++ b/docs/source/api/v5/staticdnsentries.rst
@@ -89,7 +89,7 @@ Response Structure
 :deliveryserviceId: The integral, unique identifier of a :term:`Delivery Service` under the domain of which this static DNS entry shall be active
 :host:              If ``typeId`` identifies a ``CNAME`` type record, this is an alias for the CNAME of the server, otherwise it is the Fully Qualified Domain Name (FQDN) which shall resolve to ``address``
 :id:                An integral, unique identifier for this static DNS entry
-:lastUpdated:       The date and time at which this static DNS entry was last updated
+:lastUpdated:       The date and time at which this static DNS entry was last updated, in :rfc:`3339` format
 :ttl:               The :abbr:`TTL (Time To Live)` of this static DNS entry in seconds
 :type:              The name of the type of this static DNS entry
 :typeId:            The integral, unique identifier of the :term:`Type` of this static DNS entry
@@ -118,7 +118,7 @@ Response Structure
 			"deliveryserviceId": 1,
 			"host": "test",
 			"id": 2,
-			"lastUpdated": "2018-12-10 19:59:56+00",
+			"lastUpdated": "2018-12-10T19:59:56-06:00",
 			"ttl": 300,
 			"type": "CNAME_RECORD",
 			"typeId": 41
@@ -180,7 +180,7 @@ Response Structure
 :deliveryserviceId: The integral, unique identifier of a :term:`Delivery Service` under the domain of which this static DNS entry shall be active
 :host:              If ``typeId`` identifies a ``CNAME`` type record, this is an alias for the CNAME of the server, otherwise it is the Fully Qualified Domain Name (FQDN) which shall resolve to ``address``
 :id:                An integral, unique identifier for this static DNS entry
-:lastUpdated:       The date and time at which this static DNS entry was last updated
+:lastUpdated:       The date and time at which this static DNS entry was last updated, in :rfc:`3339` format
 :ttl:               The :abbr:`TTL (Time To Live)` of this static DNS entry in seconds
 :type:              The name of the :term:`Type` of this static DNS entry
 :typeId:            The integral, unique identifier of the :term:`Type` of this static DNS entry
@@ -214,7 +214,7 @@ Response Structure
 		"deliveryserviceId": 1,
 		"host": "test",
 		"id": 2,
-		"lastUpdated": "2018-12-10 19:54:19+00",
+		"lastUpdated": "2018-12-10T19:54:19-06:00",
 		"ttl": 300,
 		"type": "CNAME_RECORD",
 		"typeId": 41
@@ -283,7 +283,7 @@ Response Structure
 :deliveryserviceId: The integral, unique identifier of a :term:`Delivery Service` under the domain of which this static DNS entry shall be active
 :host:              If ``typeId`` identifies a ``CNAME`` type record, this is an alias for the CNAME of the server, otherwise it is the :abbr:`FQDN (Fully Qualified Domain Name)` which shall resolve to ``address``
 :id:                An integral, unique identifier for this static DNS entry
-:lastUpdated:       The date and time at which this static DNS entry was last updated
+:lastUpdated:       The date and time at which this static DNS entry was last updated, in :rfc:`3339` format
 :ttl:               The :abbr:`TTL (Time To Live)` of this static DNS entry in seconds
 :type:              The name of the :term:`Type` of this static DNS entry
 :typeId:            The integral, unique identifier of the :term:`Type` of this static DNS entry
@@ -317,7 +317,7 @@ Response Structure
 		"deliveryserviceId": 1,
 		"host": "test",
 		"id": 2,
-		"lastUpdated": "2018-12-10 19:59:56+00",
+		"lastUpdated": "2018-12-10T19:59:56-06:00",
 		"ttl": 300,
 		"type": "CNAME_RECORD",
 		"typeId": 41
diff --git a/lib/go-tc/staticdnsentry.go b/lib/go-tc/staticdnsentry.go
index c867a89d77..a7d9ca6c61 100644
--- a/lib/go-tc/staticdnsentry.go
+++ b/lib/go-tc/staticdnsentry.go
@@ -1,5 +1,7 @@
 package tc
 
+import "time"
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -25,6 +27,74 @@ type StaticDNSEntriesResponse struct {
 	Alerts
 }
 
+// StaticDNSEntriesResponseV5 is a list of StaticDNSEntry as a response, for api version 5.0., for the latest
+// minor version of 5.x.
+type StaticDNSEntriesResponseV5 = StaticDNSEntriesResponseV50
+
+// StaticDNSEntriesResponseV50 is a list of StaticDNSEntry as a response, for api version 5.0.
+type StaticDNSEntriesResponseV50 struct {
+	Response []StaticDNSEntryV5 `json:"response"`
+	Alerts
+}
+
+// StaticDNSEntryV5 holds information about a static DNS entry, for the latest minor version of 5.x.
+type StaticDNSEntryV5 = StaticDNSEntryV50
+
+// StaticDNSEntryV50 holds information about a static DNS entry, for api version 5.0.
+type StaticDNSEntryV50 struct {
+
+	// The static IP Address or fqdn of the static dns entry
+	//
+	// required: true
+	Address *string `json:"address" db:"address"`
+
+	// The Cachegroup Name associated
+	//
+	CacheGroupName *string `json:"cachegroup" db:"cachegroup"`
+
+	// The Cachegroup ID associated
+	//
+	CacheGroupID *int `json:"cachegroupId" db:"cachegroup_id"`
+
+	// The DeliveryService associated
+	//
+	DeliveryService *string `json:"deliveryservice" db:"dsname"`
+
+	// The DeliveryService associated
+	//
+	// required: true
+	DeliveryServiceID *int `json:"deliveryserviceId" db:"deliveryservice_id"`
+
+	// The host of the static dns entry
+	//
+	// required: true
+	Host *string `json:"host" db:"host"`
+
+	// ID of the StaticDNSEntry
+	//
+	// required: true
+	ID *int `json:"id" db:"id"`
+
+	// LastUpdated
+	//
+	LastUpdated *time.Time `json:"lastUpdated" db:"last_updated"`
+
+	// The Time To Live for the static dns entry
+	//
+	// required: true
+	TTL *int64 `json:"ttl" db:"ttl"`
+
+	// The type of the static DNS entry
+	//
+	// enum: ["A_RECORD", "AAAA_RECORD", "CNAME_RECORD"]
+	Type *string `json:"type"`
+
+	// The type id of the static DNS entry
+	//
+	// required: true
+	TypeID *int `json:"typeId" db:"type_id"`
+}
+
 // StaticDNSEntry holds information about a static DNS entry.
 type StaticDNSEntry struct {
 
diff --git a/traffic_ops/testing/api/v5/cdn_locks_test.go b/traffic_ops/testing/api/v5/cdn_locks_test.go
index 04e36a2293..c3a2708448 100644
--- a/traffic_ops/testing/api/v5/cdn_locks_test.go
+++ b/traffic_ops/testing/api/v5/cdn_locks_test.go
@@ -463,6 +463,7 @@ func TestCDNLocks(t *testing.T) {
 						"deliveryservice": "basic-ds-in-cdn2",
 						"host":            "host2",
 						"type":            "A_RECORD",
+						"ttl":             int64(0),
 					},
 					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
 				},
@@ -475,6 +476,7 @@ func TestCDNLocks(t *testing.T) {
 						"deliveryservice": "basic-ds-in-cdn2",
 						"host":            "host2",
 						"type":            "A_RECORD",
+						"ttl":             int64(0),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
 				},
@@ -680,18 +682,20 @@ func TestCDNLocks(t *testing.T) {
 							}
 						},
 						"STATIC DNS ENTRIES POST": func(t *testing.T) {
-							staticDNSEntry := tc.StaticDNSEntry{}
+							staticDNSEntry := tc.StaticDNSEntryV5{}
 							err = json.Unmarshal(dat, &staticDNSEntry)
 							assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+							staticDNSEntry.TTL = util.Ptr(int64(0))
 							alerts, reqInf, err := testCase.ClientSession.CreateStaticDNSEntry(staticDNSEntry, testCase.RequestOpts)
 							for _, check := range testCase.Expectations {
 								check(t, reqInf, nil, alerts, err)
 							}
 						},
 						"STATIC DNS ENTRIES PUT": func(t *testing.T) {
-							staticDNSEntry := tc.StaticDNSEntry{}
+							staticDNSEntry := tc.StaticDNSEntryV5{}
 							err = json.Unmarshal(dat, &staticDNSEntry)
 							assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err)
+							staticDNSEntry.TTL = util.Ptr(int64(0))
 							alerts, reqInf, err := testCase.ClientSession.UpdateStaticDNSEntry(testCase.EndpointID(), staticDNSEntry, testCase.RequestOpts)
 							for _, check := range testCase.Expectations {
 								check(t, reqInf, nil, alerts, err)
diff --git a/traffic_ops/testing/api/v5/staticdnsentries_test.go b/traffic_ops/testing/api/v5/staticdnsentries_test.go
index 3a57e58baa..975b1b2f1d 100644
--- a/traffic_ops/testing/api/v5/staticdnsentries_test.go
+++ b/traffic_ops/testing/api/v5/staticdnsentries_test.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
 	"github.com/apache/trafficcontrol/lib/go-util/assert"
 	"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
 	"github.com/apache/trafficcontrol/traffic_ops/toclientlib"
@@ -37,7 +38,7 @@ func TestStaticDNSEntries(t *testing.T) {
 		currentTimeRFC := currentTime.Format(time.RFC1123)
 		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
 
-		methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.StaticDNSEntry]{
+		methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.StaticDNSEntryV5]{
 			"GET": {
 				"NOT MODIFIED when NO CHANGES made": {
 					ClientSession: TOSession,
@@ -60,13 +61,13 @@ func TestStaticDNSEntries(t *testing.T) {
 				"OK when VALID request": {
 					EndpointID:    GetStaticDNSEntryID(t, "host2"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "192.168.0.2",
-						CacheGroupName:  "cachegroup2",
-						DeliveryService: "ds2",
-						Host:            "host2",
-						Type:            "A_RECORD",
-						TTL:             10,
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("192.168.0.2"),
+						CacheGroupName:  util.Ptr("cachegroup2"),
+						DeliveryService: util.Ptr("ds2"),
+						Host:            util.Ptr("host2"),
+						Type:            util.Ptr("A_RECORD"),
+						TTL:             util.Ptr(int64(10)),
 					},
 					Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
 						validateStaticDNSEntriesUpdateCreateFields("host2", map[string]interface{}{"Address": "192.168.0.2"})),
@@ -74,52 +75,52 @@ func TestStaticDNSEntries(t *testing.T) {
 				"BAD REQUEST when INVALID IPV4 ADDRESS for A_RECORD": {
 					EndpointID:    GetStaticDNSEntryID(t, "host2"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "test.testdomain.net.",
-						CacheGroupName:  "cachegroup2",
-						DeliveryService: "ds2",
-						Host:            "host2",
-						Type:            "A_RECORD",
-						TTL:             10,
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("test.testdomain.net."),
+						CacheGroupName:  util.Ptr("cachegroup2"),
+						DeliveryService: util.Ptr("ds2"),
+						Host:            util.Ptr("host2"),
+						Type:            util.Ptr("A_RECORD"),
+						TTL:             util.Ptr(int64(10)),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
 				},
 				"BAD REQUEST when INVALID DNS for CNAME_RECORD": {
 					EndpointID:    GetStaticDNSEntryID(t, "host1"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
-						CacheGroupName:  "cachegroup1",
-						DeliveryService: "ds1",
-						Host:            "host1",
-						Type:            "CNAME_RECORD",
-						TTL:             0,
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+						CacheGroupName:  util.Ptr("cachegroup1"),
+						DeliveryService: util.Ptr("ds1"),
+						Host:            util.Ptr("host1"),
+						Type:            util.Ptr("CNAME_RECORD"),
+						TTL:             util.Ptr(int64(0)),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
 				},
 				"BAD REQUEST when MISSING TRAILING PERIOD for CNAME_RECORD": {
 					EndpointID:    GetStaticDNSEntryID(t, "host1"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "cdn.test.com",
-						CacheGroupName:  "cachegroup1",
-						DeliveryService: "ds1",
-						Host:            "host1",
-						Type:            "CNAME_RECORD",
-						TTL:             0,
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("cdn.test.com"),
+						CacheGroupName:  util.Ptr("cachegroup1"),
+						DeliveryService: util.Ptr("ds1"),
+						Host:            util.Ptr("host1"),
+						Type:            util.Ptr("CNAME_RECORD"),
+						TTL:             util.Ptr(int64(0)),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
 				},
 				"BAD REQUEST when INVALID IPV6 ADDRESS for AAAA_RECORD": {
 					EndpointID:    GetStaticDNSEntryID(t, "host3"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "192.168.0.1",
-						CacheGroupName:  "cachegroup2",
-						DeliveryService: "ds1",
-						Host:            "host3",
-						TTL:             10,
-						Type:            "AAAA_RECORD",
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("192.168.0.1"),
+						CacheGroupName:  util.Ptr("cachegroup2"),
+						DeliveryService: util.Ptr("ds1"),
+						Host:            util.Ptr("host3"),
+						TTL:             util.Ptr(int64(10)),
+						Type:            util.Ptr("AAAA_RECORD"),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
 				},
@@ -127,26 +128,26 @@ func TestStaticDNSEntries(t *testing.T) {
 					EndpointID:    GetStaticDNSEntryID(t, "host3"),
 					ClientSession: TOSession,
 					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfUnmodifiedSince: {currentTimeRFC}}},
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
-						CacheGroupName:  "cachegroup2",
-						DeliveryService: "ds1",
-						Host:            "host3",
-						TTL:             10,
-						Type:            "AAAA_RECORD",
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+						CacheGroupName:  util.Ptr("cachegroup2"),
+						DeliveryService: util.Ptr("ds1"),
+						Host:            util.Ptr("host3"),
+						TTL:             util.Ptr(int64(10)),
+						Type:            util.Ptr("AAAA_RECORD"),
 					},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
 				},
 				"PRECONDITION FAILED when updating with IFMATCH ETAG Header": {
 					EndpointID:    GetStaticDNSEntryID(t, "host3"),
 					ClientSession: TOSession,
-					RequestBody: tc.StaticDNSEntry{
-						Address:         "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
-						CacheGroupName:  "cachegroup2",
-						DeliveryService: "ds1",
-						Host:            "host3",
-						TTL:             10,
-						Type:            "AAAA_RECORD",
+					RequestBody: tc.StaticDNSEntryV5{
+						Address:         util.Ptr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+						CacheGroupName:  util.Ptr("cachegroup2"),
+						DeliveryService: util.Ptr("ds1"),
+						Host:            util.Ptr("host3"),
+						TTL:             util.Ptr(int64(10)),
+						Type:            util.Ptr("AAAA_RECORD"),
 					},
 					RequestOpts:  client.RequestOptions{Header: http.Header{rfc.IfMatch: {rfc.ETag(currentTime)}}},
 					Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
@@ -196,14 +197,14 @@ func TestStaticDNSEntries(t *testing.T) {
 func validateStaticDNSEntriesFields(expectedResp map[string]interface{}) utils.CkReqFunc {
 	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
 		assert.RequireNotNil(t, resp, "Expected Static DNS Entries response to not be nil.")
-		staticDNSEntriesResp := resp.([]tc.StaticDNSEntry)
+		staticDNSEntriesResp := resp.([]tc.StaticDNSEntryV5)
 		for field, expected := range expectedResp {
 			for _, staticDNSEntry := range staticDNSEntriesResp {
 				switch field {
 				case "Address":
-					assert.Equal(t, expected, staticDNSEntry.Address, "Expected Address to be %v, but got %s", expected, staticDNSEntry.Address)
+					assert.Equal(t, expected, *staticDNSEntry.Address, "Expected Address to be %v, but got %s", expected, *staticDNSEntry.Address)
 				case "Host":
-					assert.Equal(t, expected, staticDNSEntry.Host, "Expected Host to be %v, but got %s", expected, staticDNSEntry.Host)
+					assert.Equal(t, expected, *staticDNSEntry.Host, "Expected Host to be %v, but got %s", expected, *staticDNSEntry.Host)
 				default:
 					t.Errorf("Expected field: %v, does not exist in response", field)
 				}
@@ -227,9 +228,9 @@ func validateStaticDNSEntriesSort() utils.CkReqFunc {
 	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
 		assert.RequireNotNil(t, resp, "Expected Static DNS Entries response to not be nil.")
 		var staticDNSEntryHosts []string
-		staticDNSEntryResp := resp.([]tc.StaticDNSEntry)
+		staticDNSEntryResp := resp.([]tc.StaticDNSEntryV5)
 		for _, staticDNSEntry := range staticDNSEntryResp {
-			staticDNSEntryHosts = append(staticDNSEntryHosts, staticDNSEntry.Host)
+			staticDNSEntryHosts = append(staticDNSEntryHosts, *staticDNSEntry.Host)
 		}
 		assert.Equal(t, true, sort.StringsAreSorted(staticDNSEntryHosts), "List is not sorted by their hosts: %v", staticDNSEntryHosts)
 	}
@@ -242,7 +243,7 @@ func GetStaticDNSEntryID(t *testing.T, host string) func() int {
 		staticDNSEntries, _, err := TOSession.GetStaticDNSEntries(opts)
 		assert.RequireNoError(t, err, "Get Static DNS Entries Request failed with error:", err)
 		assert.RequireEqual(t, 1, len(staticDNSEntries.Response), "Expected response object length 1, but got %d", len(staticDNSEntries.Response))
-		return staticDNSEntries.Response[0].ID
+		return *staticDNSEntries.Response[0].ID
 	}
 }
 
@@ -258,11 +259,11 @@ func DeleteTestStaticDNSEntries(t *testing.T) {
 	assert.NoError(t, err, "Cannot get Static DNS Entries: %v - alerts: %+v", err, staticDNSEntries.Alerts)
 
 	for _, staticDNSEntry := range staticDNSEntries.Response {
-		alerts, _, err := TOSession.DeleteStaticDNSEntry(staticDNSEntry.ID, client.RequestOptions{})
+		alerts, _, err := TOSession.DeleteStaticDNSEntry(*staticDNSEntry.ID, client.RequestOptions{})
 		assert.NoError(t, err, "Unexpected error deleting Static DNS Entry '%s' (#%d): %v - alerts: %+v", staticDNSEntry.Host, staticDNSEntry.ID, err, alerts.Alerts)
 		// Retrieve the Static DNS Entry to see if it got deleted
 		opts := client.NewRequestOptions()
-		opts.QueryParameters.Set("host", staticDNSEntry.Host)
+		opts.QueryParameters.Set("host", *staticDNSEntry.Host)
 		getStaticDNSEntry, _, err := TOSession.GetStaticDNSEntries(opts)
 		assert.NoError(t, err, "Error getting Static DNS Entry '%s' after deletion: %v - alerts: %+v", staticDNSEntry.Host, err, getStaticDNSEntry.Alerts)
 		assert.Equal(t, 0, len(getStaticDNSEntry.Response), "Expected Static DNS Entry '%s' to be deleted, but it was found in Traffic Ops", staticDNSEntry.Host)
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go b/traffic_ops/testing/api/v5/traffic_control_test.go
index 6fa619b368..6112ea16ac 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -50,7 +50,7 @@ type TrafficControl struct {
 	ServerCapabilities                                []tc.ServerCapabilityV5                 `json:"serverCapabilities"`
 	ServiceCategories                                 []tc.ServiceCategoryV5                  `json:"serviceCategories"`
 	Statuses                                          []tc.StatusV5                           `json:"statuses"`
-	StaticDNSEntries                                  []tc.StaticDNSEntry                     `json:"staticdnsentries"`
+	StaticDNSEntries                                  []tc.StaticDNSEntryV5                   `json:"staticdnsentries"`
 	StatsSummaries                                    []tc.StatsSummaryV5                     `json:"statsSummaries"`
 	Tenants                                           []tc.Tenant                             `json:"tenants"`
 	ServerCheckExtensions                             []tc.ServerCheckExtensionNullable       `json:"servercheck_extensions"`
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index 8412bf74f7..7f2c8ecbbd 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -428,10 +428,10 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `service_categories/{name}$`, Handler: servicecategory.DeleteServiceCategory, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVICE-CATEGORY:DELETE", "SERVICE-CATEGORY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 43253822381},
 
 		//StaticDNSEntries
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `staticdnsentries/?$`, Handler: api.ReadHandler(&staticdnsentry.TOStaticDNSEntry{}), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42893947731},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `staticdnsentries/?$`, Handler: api.UpdateHandler(&staticdnsentry.TOStaticDNSEntry{}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:UPDATE", "STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 44245711131},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `staticdnsentries/?$`, Handler: api.CreateHandler(&staticdnsentry.TOStaticDNSEntry{}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:CREATE", "STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 462914823831},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `staticdnsentries/?$`, Handler: api.DeleteHandler(&staticdnsentry.TOStaticDNSEntry{}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:DELETE", "STATIC-DN:READ", "DELIVERY-SERVICE:READ", "CACHE-GROUP:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 484603113231},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `staticdnsentries/?$`, Handler: staticdnsentry.Get, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42893947731},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `staticdnsentries/?$`, Handler: staticdnsentry.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:UPDATE", "STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 44245711131},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `staticdnsentries/?$`, Handler: staticdnsentry.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:CREATE", "STATIC-DN:READ", "CACHE-GROUP:READ", "DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 462914823831},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `staticdnsentries/?$`, Handler: staticdnsentry.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"STATIC-DN:DELETE", "STATIC-DN:READ", "DELIVERY-SERVICE:READ", "CACHE-GROUP:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 484603113231},
 
 		//ProfileParameters
 		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `profiles/{id}/parameters/?$`, Handler: profileparameter.GetProfileID, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"PROFILE:READ", "PARAMETER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 47646497531},
diff --git a/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go b/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
index 71459e3272..3db3282393 100644
--- a/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
+++ b/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
@@ -20,17 +20,22 @@ package staticdnsentry
  */
 
 import (
+	"database/sql"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
 	"strconv"
 	"time"
 
+	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
 	"github.com/apache/trafficcontrol/lib/go-util"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
+
 	validation "github.com/go-ozzo/ozzo-validation"
 	"github.com/go-ozzo/ozzo-validation/is"
 )
@@ -260,3 +265,380 @@ func deleteQuery() string {
 WHERE id=:id`
 	return query
 }
+
+// Validate validates a statisDNSEntry entity and makes sure that all the supplied fields are valid.
+func Validate(staticDNSEntry tc.StaticDNSEntryV5, tx *sql.Tx) (error, error) {
+	typeStr, err := tc.ValidateTypeID(tx, staticDNSEntry.TypeID, "staticdnsentry")
+	if err != nil {
+		return err, nil
+	}
+
+	var addressErr, ttlErr error
+	switch typeStr {
+	case "A_RECORD":
+		addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.IPv4)
+	case "AAAA_RECORD":
+		addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.IPv6)
+	case "CNAME_RECORD":
+		addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.DNSName)
+		address := *staticDNSEntry.Address
+		if addressErr == nil {
+			lastChar := address[len(address)-1:]
+			if lastChar != "." {
+				addressErr = fmt.Errorf("for type: CNAME_RECORD must have a trailing period")
+			}
+		}
+	default:
+		addressErr = validation.Validate(staticDNSEntry.Address, validation.Required)
+	}
+
+	if staticDNSEntry.TTL != nil {
+		if *staticDNSEntry.TTL == 0 {
+			ttlErr = validation.Validate(staticDNSEntry.TTL, is.Digit)
+		}
+	} else {
+		ttlErr = validation.Validate(staticDNSEntry.TTL, validation.Required)
+	}
+
+	errs := validation.Errors{
+		"host":              validation.Validate(staticDNSEntry.Host, validation.Required, is.DNSName),
+		"address":           addressErr,
+		"deliveryserviceId": validation.Validate(staticDNSEntry.DeliveryServiceID, validation.Required),
+		"ttl":               ttlErr,
+		"typeId":            validation.Validate(staticDNSEntry.TypeID, validation.Required),
+	}
+	return util.JoinErrs(tovalidate.ToErrors(errs)), nil
+}
+
+// Update will modify an existing StaticDNSEntry entity in the database, for api v5.0.
+func Update(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	tx := inf.Tx.Tx
+
+	defer r.Body.Close()
+	var staticDNSEntry tc.StaticDNSEntryV5
+
+	if err := json.NewDecoder(r.Body).Decode(&staticDNSEntry); err != nil {
+		api.HandleErr(w, r, nil, http.StatusInternalServerError, nil, err)
+		return
+	}
+
+	userErr, sysErr = Validate(staticDNSEntry, tx)
+	if userErr != nil || sysErr != nil {
+		code := http.StatusBadRequest
+		if sysErr != nil {
+			code = http.StatusInternalServerError
+		}
+		api.HandleErr(w, r, tx, code, userErr, sysErr)
+		return
+	}
+
+	var cdnName tc.CDNName
+	if id, ok := inf.Params["id"]; !ok {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("missing key: id"), nil)
+		return
+	} else {
+		idNum, err := strconv.Atoi(id)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("couldn't convert ID into a numeric value: "+err.Error()), nil)
+			return
+		}
+		staticDNSEntry.ID = &idNum
+		if staticDNSEntry.DeliveryServiceID != nil {
+			_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, *staticDNSEntry.DeliveryServiceID)
+			if err != nil {
+				api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err)
+				return
+			}
+			userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName)
+			if userErr != nil || sysErr != nil {
+				api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+				return
+			}
+		}
+
+		existingLastUpdated, found, err := api.GetLastUpdated(inf.Tx, idNum, "staticdnsentry")
+		if err == nil && found == false {
+			api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no staticDNSEntry found with this id"), nil)
+			return
+		}
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusNotFound, nil, err)
+			return
+		}
+		if !api.IsUnmodified(r.Header, *existingLastUpdated) {
+			api.HandleErr(w, r, tx, http.StatusPreconditionFailed, api.ResourceModifiedError, nil)
+			return
+		}
+
+		rows, err := inf.Tx.NamedQuery(updateQuery(), staticDNSEntry)
+		if err != nil {
+			userErr, sysErr, errCode = api.ParseDBError(err)
+			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+			return
+		}
+		defer rows.Close()
+
+		if !rows.Next() {
+			api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no staticDNSEntry found with this id"), nil)
+			return
+		}
+		lastUpdated := time.Time{}
+		if err := rows.Scan(&lastUpdated); err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning lastUpdated from staticDNSEntry insert: "+err.Error()))
+			return
+		}
+		staticDNSEntry.LastUpdated = &lastUpdated
+		if rows.Next() {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry update affected too many rows: >1"))
+			return
+		}
+
+		alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was updated.")
+		api.WriteAlertsObj(w, r, http.StatusOK, alerts, staticDNSEntry)
+		changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %s, ID: %d, ACTION: Updated staticDNSEntry", *staticDNSEntry.Host, *staticDNSEntry.ID)
+		api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+		return
+	}
+}
+
+// Create will add a new StaticDNSEntry entity into the database, for api v5.0.
+func Create(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	tx := inf.Tx.Tx
+
+	defer r.Body.Close()
+	var staticDNSEntry tc.StaticDNSEntryV5
+
+	if err := json.NewDecoder(r.Body).Decode(&staticDNSEntry); err != nil {
+		api.HandleErr(w, r, nil, http.StatusInternalServerError, nil, err)
+		return
+	}
+
+	userErr, sysErr = Validate(staticDNSEntry, tx)
+	if userErr != nil || sysErr != nil {
+		code := http.StatusBadRequest
+		if sysErr != nil {
+			code = http.StatusInternalServerError
+		}
+		api.HandleErr(w, r, tx, code, userErr, sysErr)
+		return
+	}
+
+	var cdnName tc.CDNName
+	var err error
+	if staticDNSEntry.DeliveryServiceID != nil {
+		_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, *staticDNSEntry.DeliveryServiceID)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err)
+			return
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+			return
+		}
+	}
+
+	resultRows, err := inf.Tx.NamedQuery(insertQuery(), staticDNSEntry)
+	if err != nil {
+		userErr, sysErr, errCode = api.ParseDBError(err)
+		api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+		return
+	}
+	defer resultRows.Close()
+
+	var id int
+	lastUpdated := time.Time{}
+	rowsAffected := 0
+	for resultRows.Next() {
+		rowsAffected++
+		if err := resultRows.Scan(&id, &lastUpdated); err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry create scanning: "+err.Error()))
+			return
+		}
+	}
+
+	if rowsAffected == 0 {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry create: no staticDNSEntry was inserted, no id was returned"))
+		return
+	} else if rowsAffected > 1 {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("too many ids returned from staticDNSEntry insert"))
+		return
+	}
+	staticDNSEntry.ID = &id
+	staticDNSEntry.LastUpdated = &lastUpdated
+
+	alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was created.")
+	api.WriteAlertsObj(w, r, http.StatusOK, alerts, staticDNSEntry)
+	changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %s, ID: %d, ACTION: Created staticDNSEntry", *staticDNSEntry.Host, *staticDNSEntry.ID)
+	api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+	return
+}
+
+// Delete removes a staticDNSEntry from the database, for api v5.0.
+func Delete(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	tx := inf.Tx.Tx
+	if id, ok := inf.Params["id"]; !ok {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("missing key: id"), nil)
+		return
+	} else {
+		idNum, err := strconv.Atoi(id)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("couldn't convert ID into a numeric value: "+err.Error()), nil)
+			return
+		}
+		staticDNSEntry := tc.StaticDNSEntryV5{
+			ID: &idNum,
+		}
+		var cdnName tc.CDNName
+		var dsID int
+		if staticDNSEntry.DeliveryServiceID != nil {
+			dsID = *staticDNSEntry.DeliveryServiceID
+		} else if staticDNSEntry.ID != nil {
+			dsID, err = dbhelpers.GetDSIDFromStaticDNSEntry(tx, *staticDNSEntry.ID)
+			if err != nil {
+				api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("couldn't get DS ID from static dns entry ID: "+err.Error()))
+				return
+			}
+		}
+		_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, dsID)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err)
+			return
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+			return
+		}
+		result, err := inf.Tx.NamedExec(deleteQuery(), staticDNSEntry)
+		if err != nil {
+			userErr, sysErr, errCode = api.ParseDBError(err)
+			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+			return
+		}
+
+		if rowsAffected, err := result.RowsAffected(); err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deleting staticDNSEntry: getting rows affected: "+err.Error()))
+			return
+		} else if rowsAffected < 1 {
+			api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("no staticDNSEntry with that key found"), nil)
+			return
+		} else if rowsAffected > 1 {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("staticDNSEntry delete affected too many rows: %d", rowsAffected))
+			return
+		}
+		log.Debugf("changelog for delete on staticDNSEntry")
+
+		alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was deleted.")
+		api.WriteAlerts(w, r, http.StatusOK, alerts)
+		changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %d, ID: %d, ACTION: Deleted staticDNSEntry", *staticDNSEntry.ID, *staticDNSEntry.ID)
+		api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+		return
+	}
+}
+
+// Get : function to read the staticDNSEntries, for api version 5.0.
+func Get(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	api.DefaultSort(inf, "host")
+	useIMS := false
+	config, e := api.GetConfig(r.Context())
+	if e == nil && config != nil {
+		useIMS = config.UseIMS
+	} else {
+		log.Warnf("Couldn't get config %v", e)
+	}
+
+	var maxTime time.Time
+	var runSecond bool
+
+	scList := make([]tc.StaticDNSEntryV5, 0)
+
+	tx := inf.Tx
+
+	where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, map[string]dbhelpers.WhereColumnInfo{
+		"address":           dbhelpers.WhereColumnInfo{Column: "sde.address"},
+		"cachegroup":        dbhelpers.WhereColumnInfo{Column: "cg.name"},
+		"cachegroupId":      dbhelpers.WhereColumnInfo{Column: "cg.id"},
+		"deliveryservice":   dbhelpers.WhereColumnInfo{Column: "ds.xml_id"},
+		"deliveryserviceId": dbhelpers.WhereColumnInfo{Column: "sde.deliveryservice"},
+		"host":              dbhelpers.WhereColumnInfo{Column: "sde.host"},
+		"id":                dbhelpers.WhereColumnInfo{Column: "sde.id"},
+		"ttl":               dbhelpers.WhereColumnInfo{Column: "sde.ttl"},
+		"type":              dbhelpers.WhereColumnInfo{Column: "tp.name"},
+		"typeId":            dbhelpers.WhereColumnInfo{Column: "tp.id"},
+	})
+	if len(errs) > 0 {
+		api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, nil, util.JoinErrs(errs))
+		return
+	}
+	if useIMS {
+		runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx, r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+		if !runSecond {
+			log.Debugln("IMS HIT")
+			api.AddLastModifiedHdr(w, maxTime)
+			w.WriteHeader(http.StatusNotModified)
+			return
+		}
+		log.Debugln("IMS MISS")
+	} else {
+		log.Debugln("Non IMS request")
+	}
+
+	// Case where we need to run the second query
+	query := selectQuery() + where + orderBy + pagination
+	rows, err := tx.NamedQuery(query, queryValues)
+	if err != nil {
+		api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, errors.New("querying static DNS entries: "+err.Error()))
+		return
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		v := tc.StaticDNSEntryV5{}
+		if err = rows.StructScan(&v); err != nil {
+			api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, errors.New("scanning static DNS entries: "+err.Error()))
+			return
+		}
+		scList = append(scList, v)
+	}
+
+	api.WriteResp(w, r, scList)
+}
+
+func selectMaxLastUpdatedQuery(where string) string {
+	return `SELECT max(t) from (
+		SELECT max(sde.last_updated) as t FROM staticdnsentry as sde
+JOIN type as tp on sde.type = tp.id
+LEFT JOIN cachegroup as cg ON sde.cachegroup = cg.id
+JOIN deliveryservice as ds on sde.deliveryservice = ds.id ` + where +
+		` UNION ALL
+	select max(last_updated) as t from last_deleted l where l.table_name='staticdnsentry') as res`
+}
diff --git a/traffic_ops/v5-client/staticdnsentry.go b/traffic_ops/v5-client/staticdnsentry.go
index dee4fe520d..63397884a4 100644
--- a/traffic_ops/v5-client/staticdnsentry.go
+++ b/traffic_ops/v5-client/staticdnsentry.go
@@ -28,63 +28,63 @@ import (
 // endpoint.
 const apiStaticDNSEntries = "/staticdnsentries"
 
-func staticDNSEntryIDs(to *Session, sdns *tc.StaticDNSEntry) error {
+func staticDNSEntryIDsV5(to *Session, sdns *tc.StaticDNSEntryV5) error {
 	if sdns == nil {
 		return errors.New("cannot resolve names to IDs for nil StaticDNSEntry")
 	}
-	if sdns.CacheGroupID == 0 && sdns.CacheGroupName != "" {
+	if (sdns.CacheGroupID == nil || *sdns.CacheGroupID == 0) && (sdns.CacheGroupName != nil && *sdns.CacheGroupName != "") {
 		opts := NewRequestOptions()
-		opts.QueryParameters.Set("name", sdns.CacheGroupName)
+		opts.QueryParameters.Set("name", *sdns.CacheGroupName)
 		p, _, err := to.GetCacheGroups(opts)
 		if err != nil {
 			return err
 		}
 		if len(p.Response) == 0 {
-			return errors.New("no CacheGroup named " + sdns.CacheGroupName)
+			return errors.New("no CacheGroup named " + *sdns.CacheGroupName)
 		}
 		if p.Response[0].ID == nil {
-			return errors.New("CacheGroup named " + sdns.CacheGroupName + " has a nil ID")
+			return errors.New("CacheGroup named " + *sdns.CacheGroupName + " has a nil ID")
 		}
-		sdns.CacheGroupID = *p.Response[0].ID
+		sdns.CacheGroupID = p.Response[0].ID
 	}
 
-	if sdns.DeliveryServiceID == 0 && sdns.DeliveryService != "" {
+	if (sdns.DeliveryServiceID == nil || *sdns.DeliveryServiceID == 0) && (sdns.DeliveryService != nil && *sdns.DeliveryService != "") {
 		opts := NewRequestOptions()
-		opts.QueryParameters.Set("xmlId", sdns.DeliveryService)
+		opts.QueryParameters.Set("xmlId", *sdns.DeliveryService)
 		dses, _, err := to.GetDeliveryServices(opts)
 		if err != nil {
 			return err
 		}
 		if len(dses.Response) == 0 {
-			return errors.New("no deliveryservice with name " + sdns.DeliveryService)
+			return errors.New("no deliveryservice with name " + *sdns.DeliveryService)
 		}
 		if dses.Response[0].ID == nil {
-			return errors.New("Deliveryservice with name " + sdns.DeliveryService + " has a nil ID")
+			return errors.New("Deliveryservice with name " + *sdns.DeliveryService + " has a nil ID")
 		}
-		sdns.DeliveryServiceID = *dses.Response[0].ID
+		sdns.DeliveryServiceID = dses.Response[0].ID
 	}
 
-	if sdns.TypeID == 0 && sdns.Type != "" {
+	if (sdns.TypeID == nil || *sdns.TypeID == 0) && (sdns.Type != nil && *sdns.Type != "") {
 		opts := NewRequestOptions()
-		opts.QueryParameters.Set("name", sdns.Type)
+		opts.QueryParameters.Set("name", *sdns.Type)
 		types, _, err := to.GetTypes(opts)
 		if err != nil {
 			return err
 		}
 		if len(types.Response) == 0 {
-			return errors.New("no type with name " + sdns.Type)
+			return errors.New("no type with name " + *sdns.Type)
 		}
-		sdns.TypeID = types.Response[0].ID
+		sdns.TypeID = &types.Response[0].ID
 	}
 
 	return nil
 }
 
 // CreateStaticDNSEntry creates the given Static DNS Entry.
-func (to *Session) CreateStaticDNSEntry(sdns tc.StaticDNSEntry, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
+func (to *Session) CreateStaticDNSEntry(sdns tc.StaticDNSEntryV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
 	// fill in missing IDs from names
 	var alerts tc.Alerts
-	err := staticDNSEntryIDs(to, &sdns)
+	err := staticDNSEntryIDsV5(to, &sdns)
 	if err != nil {
 		return alerts, toclientlib.ReqInf{CacheHitStatus: toclientlib.CacheHitStatusMiss}, err
 	}
@@ -94,10 +94,10 @@ func (to *Session) CreateStaticDNSEntry(sdns tc.StaticDNSEntry, opts RequestOpti
 
 // UpdateStaticDNSEntry replaces the Static DNS Entry identified by 'id' with
 // the one provided.
-func (to *Session) UpdateStaticDNSEntry(id int, sdns tc.StaticDNSEntry, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
+func (to *Session) UpdateStaticDNSEntry(id int, sdns tc.StaticDNSEntryV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
 	// fill in missing IDs from names
 	var alerts tc.Alerts
-	err := staticDNSEntryIDs(to, &sdns)
+	err := staticDNSEntryIDsV5(to, &sdns)
 	if err != nil {
 		return alerts, toclientlib.ReqInf{CacheHitStatus: toclientlib.CacheHitStatusMiss}, err
 	}
@@ -110,8 +110,8 @@ func (to *Session) UpdateStaticDNSEntry(id int, sdns tc.StaticDNSEntry, opts Req
 }
 
 // GetStaticDNSEntries retrieves all Static DNS Entries stored in Traffic Ops.
-func (to *Session) GetStaticDNSEntries(opts RequestOptions) (tc.StaticDNSEntriesResponse, toclientlib.ReqInf, error) {
-	var data tc.StaticDNSEntriesResponse
+func (to *Session) GetStaticDNSEntries(opts RequestOptions) (tc.StaticDNSEntriesResponseV5, toclientlib.ReqInf, error) {
+	var data tc.StaticDNSEntriesResponseV5
 	reqInf, err := to.get(apiStaticDNSEntries, opts, &data)
 	return data, reqInf, err
 }