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

ocket8888 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 ccff78a  Rewrite POST servercheck to GO (#3935)
ccff78a is described below

commit ccff78ae768bae9444b8cad787506f57fdae6619
Author: Michael Hoppal <[email protected]>
AuthorDate: Thu Oct 3 09:16:43 2019 -0600

    Rewrite POST servercheck to GO (#3935)
    
    * Rewrite POST serverchecks to GO
    
    * License headers missing on some go files
    
    * Address PR review
    
    * Fix to_extension active type
    
    * Update serverchecks_test.go
    
    * Fix db_helper for ats
    
    * Fix tests
---
 docs/source/api/servercheck.rst                    |  15 ++-
 lib/go-tc/cachegroupfallback.go                    |   5 -
 lib/go-tc/log.go                                   |  12 +-
 lib/go-tc/serverchecks.go                          |  57 +++++----
 lib/go-tc/toextension.go                           |  41 +++++++
 traffic_ops/client/servercheck.go                  |  11 +-
 traffic_ops/client/toextension.go                  |  69 +++++++++++
 traffic_ops/testing/api/conf/traffic-ops-test.conf |   3 +-
 traffic_ops/testing/api/config/config.go           |   3 +
 traffic_ops/testing/api/v14/serverchecks_test.go   | 132 +++++++++++++++++++++
 traffic_ops/testing/api/v14/tc-fixtures.json       |  34 ++++++
 traffic_ops/testing/api/v14/todb.go                |  42 +++++++
 traffic_ops/testing/api/v14/toextension_test.go    |  98 +++++++++++++++
 traffic_ops/testing/api/v14/traffic_control.go     |   2 +
 traffic_ops/testing/api/v14/withobjs.go            |   4 +
 traffic_ops/traffic_ops_golang/ats/db.go           |   4 +-
 .../traffic_ops_golang/dbhelpers/db_helpers.go     |  12 +-
 .../deliveryservice/servers/delete.go              |   2 +-
 traffic_ops/traffic_ops_golang/routing/routes.go   |   6 +-
 .../server/servers_assignment.go                   |   2 +-
 .../traffic_ops_golang/servercheck/servercheck.go  | 130 ++++++++++++++++++++
 21 files changed, 630 insertions(+), 54 deletions(-)

diff --git a/docs/source/api/servercheck.rst b/docs/source/api/servercheck.rst
index 55d81fb..f2f30dd 100644
--- a/docs/source/api/servercheck.rst
+++ b/docs/source/api/servercheck.rst
@@ -18,7 +18,8 @@
 ***************
 ``servercheck``
 ***************
-.. caution:: This page is a stub! Much of it may be missing or just downright 
wrong - it needs a lot of love from people with the domain knowledge required 
to update it.
+
+Updates the resulting value from running a given check extension on a server.
 
 ``POST``
 ========
@@ -26,13 +27,15 @@ Post a server check result to the "serverchecks" table.
 
 :Auth. Required: Yes
 :Roles Required: None\ [1]_
-:Response Type:  ``undefined``
+:Response Type: Object
 
 Request Structure
 -----------------
-:host_name:              The hostname of the server to which this 
"servercheck" refers
-:id:                     An integral, unique identifier for this "servercheck"
-:servercheck_short_name: The short name for the check for which the 
"servercheck" has been measured
+The request only requires to have either ``host_name`` or ``id`` defined.
+
+:host_name:              The hostname of the server to which this 
"servercheck" refers.
+:id:                     The id of the server to which this "servercheck" 
refers.
+:servercheck_short_name: The short name of the "servercheck".
 :value:                  The value of the "servercheck"
 
 .. code-block:: http
@@ -50,7 +53,7 @@ Request Structure
                "id": 1,
                "host_name": "edge",
                "servercheck_short_name": "test",
-               "value": "A test servercheck for API examples"
+               "value": 1
        }
 
 Response Structure
diff --git a/lib/go-tc/cachegroupfallback.go b/lib/go-tc/cachegroupfallback.go
index ecd1d8c..f06ed28 100644
--- a/lib/go-tc/cachegroupfallback.go
+++ b/lib/go-tc/cachegroupfallback.go
@@ -1,6 +1,5 @@
 package tc
 
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -38,18 +37,14 @@ type CacheGroupFallbackResponse struct {
 
 // CacheGroupFallback ...
 type CacheGroupFallback struct {
-
        PrimaryCgId int `json:"primaryId" db:"primary_cg"`
        BackupCgId  int `json:"backupId" db:"backup_cg"`
        SetOrder    int `json:"setOrder" db:"set_order"`
-
 }
 
 // CacheGroupFallbackNullable ...
 type CacheGroupFallbackNullable struct {
-
        PrimaryCgId *int `json:"primaryId" db:"primary_cg"`
        BackupCgId  *int `json:"backupId" db:"backup_cg"`
        SetOrder    *int `json:"setOrder" db:"set_order"`
 }
-
diff --git a/lib/go-tc/log.go b/lib/go-tc/log.go
index 7ba8a3e..ab8a12c 100644
--- a/lib/go-tc/log.go
+++ b/lib/go-tc/log.go
@@ -24,12 +24,12 @@ type LogsResponse struct {
 }
 
 type Log struct {
-       ID           *int       `json:"id"`
-       LastUpdated  *Time      `json:"lastUpdated"`
-       Level        *string    `json:"level"`
-       Message      *string    `json:"message"`
-       TicketNum    *int       `json:"ticketNum"`
-       User         *string    `json:"user"`
+       ID          *int    `json:"id"`
+       LastUpdated *Time   `json:"lastUpdated"`
+       Level       *string `json:"level"`
+       Message     *string `json:"message"`
+       TicketNum   *int    `json:"ticketNum"`
+       User        *string `json:"user"`
 }
 
 type NewLogCountResp struct {
diff --git a/lib/go-tc/serverchecks.go b/lib/go-tc/serverchecks.go
index 671028a..c0e58eb 100644
--- a/lib/go-tc/serverchecks.go
+++ b/lib/go-tc/serverchecks.go
@@ -1,5 +1,13 @@
 package tc
 
+import (
+       "database/sql"
+       "errors"
+       "strings"
+
+       "github.com/apache/trafficcontrol/lib/go-util"
+)
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -19,15 +27,6 @@ package tc
  * under the License.
  */
 
-// Health monitor checks for servers
-// A Single Servercheck Response for Update and Create to depict what changed
-// swagger:response ServercheckResponse
-// in: body
-type ServercheckResponse struct {
-       // in: body
-       Response Servercheck `json:"response"`
-}
-
 // A list of Servercheck Responses
 // swagger:response ServerchecksResponse
 // in: body
@@ -108,7 +107,6 @@ type Servercheck struct {
 }
 
 // A Single Servercheck struct for Update and Create to depict what changed
-// swagger:model ServercheckPost
 type ServercheckPost struct {
 
        // The Servercheck data to submit
@@ -116,29 +114,46 @@ type ServercheckPost struct {
        // Name of the server check type
        //
        // required: true
-       Name string `json:"servercheck_short_name" db:"servercheck_short_name"`
+       Name string `json:"servercheck_short_name"`
 
        // ID of the server
        //
-       // required: true
-       ID int `json:"id" db:"id"`
+       ID int `json:"id"`
 
        // Name of the server
-       HostName string `json:"name" db:"name"`
+       HostName string `json:"name" `
 
        // Value of the check result
        //
        // required: true
-       Value int `json:"value" db:"value"`
+       Value int `json:"value"`
+}
+
+type ServercheckRequestNullable struct {
+       Name     *string `json:"servercheck_short_name"`
+       ID       *int    `json:"id"`
+       Value    *int    `json:"value"`
+       HostName *string `json:"host_name"`
 }
 
-type ServercheckPostNullable struct {
-       Name  string `json:"servercheck_short_name" db:"servercheck_short_name"`
-       ID    int    `json:"id" db:"id"`
-       Value int    `json:"value" db:"value"`
+// Validate ServercheckRequestNullable
+func (scp ServercheckRequestNullable) Validate(tx *sql.Tx) error {
+       errs := []string{}
+
+       if scp.ID == nil && scp.HostName == nil {
+               errs = append(errs, "id or host_name")
+       }
+
+       if scp.Name == nil || *scp.Name == "" {
+               errs = append(errs, "servercheck_short_name")
+       }
+
+       if len(errs) > 0 {
+               return util.JoinErrs([]error{errors.New("required fields 
missing: " + strings.Join(errs, ", "))})
+       }
+       return nil
 }
 
 type ServercheckPostResponse struct {
-       Alerts   []Alert                 `json:"alerts"`
-       Response DeliveryServiceUserPost `json:"response"`
+       Alerts []Alert `json:"alerts"`
 }
diff --git a/lib/go-tc/toextension.go b/lib/go-tc/toextension.go
new file mode 100644
index 0000000..fb4c969
--- /dev/null
+++ b/lib/go-tc/toextension.go
@@ -0,0 +1,41 @@
+package tc
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import "encoding/json"
+
+// TOExtensionNullable represents a servercheck extension used by Traffic Ops
+type TOExtensionNullable struct {
+       ID                   *int            `json:"id"`
+       Name                 *string         `json:"name"`
+       Version              *string         `json:"version"`
+       InfoURL              *string         `json:"info_url"`
+       ScriptFile           *string         `json:"script_file"`
+       IsActive             *int            `json:"isactive"`
+       AdditionConfigJSON   json.RawMessage `json:"additional_config_json"`
+       Description          *string         `json:"description"`
+       ServercheckShortName *string         `json:"servercheck_short_name"`
+       Type                 *string         `json:"type"`
+}
+
+// TOExtensionResponse represents the response from Traffic Ops when creating 
a TOExtension
+type TOExtensionResponse struct {
+       Response []TOExtensionNullable `json:"response"`
+}
diff --git a/traffic_ops/client/servercheck.go 
b/traffic_ops/client/servercheck.go
index d44e74a..327ccbe 100644
--- a/traffic_ops/client/servercheck.go
+++ b/traffic_ops/client/servercheck.go
@@ -1,8 +1,11 @@
 /*
+
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
+
    http://www.apache.org/licenses/LICENSE-2.0
+
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,8 +25,8 @@ import (
 const API_V13_SERVERCHECK = "/api/1.3/servercheck"
 const API_V13_SERVERCHECK_GET = "/api/1.3/servers/checks"
 
-// Update a Server Check Status
-func (to *Session) UpdateCheckStatus(status tc.ServercheckPostNullable) 
(*tc.ServercheckPostResponse, ReqInf, error) {
+// InsertServerCheckStatus Will insert/update the servercheck value based on 
if it already exists or not
+func (to *Session) InsertServerCheckStatus(status 
tc.ServercheckRequestNullable) (*tc.ServercheckPostResponse, ReqInf, error) {
        uri := API_V13_SERVERCHECK
        var remoteAddr net.Addr
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
@@ -39,8 +42,8 @@ func (to *Session) UpdateCheckStatus(status 
tc.ServercheckPostNullable) (*tc.Ser
        return &resp, reqInf, nil
 }
 
-// Get Server Check Data
-func (to *Session) GetCheckData() (*tc.ServerchecksResponse, ReqInf, error) {
+// GetServerChecks Gets ServerChecks Data
+func (to *Session) GetServerChecks() (*tc.ServerchecksResponse, ReqInf, error) 
{
        uri := API_V13_SERVERCHECK_GET
        var remoteAddr net.Addr
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
diff --git a/traffic_ops/client/toextension.go 
b/traffic_ops/client/toextension.go
new file mode 100644
index 0000000..c064f04
--- /dev/null
+++ b/traffic_ops/client/toextension.go
@@ -0,0 +1,69 @@
+/*
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+   http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package client
+
+import (
+       "encoding/json"
+       "fmt"
+       "net"
+       "net/http"
+
+       "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+const API_V14_TO_EXTENSION = "/api/1.4/to_extensions"
+
+// CreateTOExtension creates a to_extension
+func (to *Session) CreateTOExtension(toExtension tc.TOExtensionNullable) 
(tc.Alerts, ReqInf, error) {
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(toExtension)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       resp, remoteAddr, err := to.request(http.MethodPost, 
API_V14_TO_EXTENSION, reqBody)
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, err
+}
+
+// DeleteToExtension deletes a to_extension
+func (to *Session) DeleteTOExtension(id int) (tc.Alerts, ReqInf, error) {
+       URI := fmt.Sprintf("%s/%d/delete", API_V14_TO_EXTENSION, id)
+       resp, remoteAddr, err := to.request(http.MethodPost, URI, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, err
+}
+
+// GetTOExtensions gets all to_extensions
+func (to *Session) GetTOExtensions() (tc.TOExtensionResponse, ReqInf, error) {
+       resp, remoteAddr, err := to.request(http.MethodGet, 
API_V14_TO_EXTENSION, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.TOExtensionResponse{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var toExtResp tc.TOExtensionResponse
+       err = json.NewDecoder(resp.Body).Decode(&toExtResp)
+       return toExtResp, reqInf, err
+}
diff --git a/traffic_ops/testing/api/conf/traffic-ops-test.conf 
b/traffic_ops/testing/api/conf/traffic-ops-test.conf
index 0ae53b4..5803f44 100644
--- a/traffic_ops/testing/api/conf/traffic-ops-test.conf
+++ b/traffic_ops/testing/api/conf/traffic-ops-test.conf
@@ -20,7 +20,8 @@
             "admin": "admin",
             "federation": "federation",
             "portal": "portal",
-            "readOnly": "readOnly"
+            "readOnly": "readOnly",
+            "extension": "extension"
         }
     },
     "trafficOpsDB": {
diff --git a/traffic_ops/testing/api/config/config.go 
b/traffic_ops/testing/api/config/config.go
index 957cd20..af266cf 100644
--- a/traffic_ops/testing/api/config/config.go
+++ b/traffic_ops/testing/api/config/config.go
@@ -72,6 +72,9 @@ type Users struct {
 
        // FederationUser - The Traffic Ops Federation user
        Federation string `json:"federation" envconfig:"TO_USER_FEDERATION"`
+
+       // Extension - The Traffic Ops Extension user
+       Extension string `json:"extension" envconfig:"TO_USER_EXTENSION"`
 }
 
 // TrafficOpsDB - config section
diff --git a/traffic_ops/testing/api/v14/serverchecks_test.go 
b/traffic_ops/testing/api/v14/serverchecks_test.go
new file mode 100644
index 0000000..338711d
--- /dev/null
+++ b/traffic_ops/testing/api/v14/serverchecks_test.go
@@ -0,0 +1,132 @@
+package v14
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+       "testing"
+       "time"
+
+       "github.com/apache/trafficcontrol/lib/go-log"
+       "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func TestServerChecks(t *testing.T) {
+       WithObjs(t, []TCObj{CDNs, Types, Parameters, Profiles, Statuses, 
Divisions, Regions, PhysLocations, CacheGroups, Servers, TOExtensions, 
ServerChecks}, func() {
+               CreateTestInvalidServerChecks(t)
+               UpdateTestServerChecks(t)
+               GetTestServerChecks(t)
+       })
+}
+
+func CreateTestServerChecks(t *testing.T) {
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
+
+       for _, servercheck := range testData.Serverchecks {
+               resp, _, err := TOSession.InsertServerCheckStatus(servercheck)
+               log.Debugf("Response: %v host_name %v check %v", 
*servercheck.HostName, *servercheck.Name, resp)
+               if err != nil {
+                       t.Errorf("could not CREATE servercheck: %v\n", err)
+               }
+       }
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
+}
+
+func CreateTestInvalidServerChecks(t *testing.T) {
+       toReqTimeout := time.Second * 
time.Duration(Config.Default.Session.TimeoutInSecs)
+
+       _, _, err := TOSession.InsertServerCheckStatus(testData.Serverchecks[0])
+       if err == nil {
+               t.Errorf("expected to receive error with non extension user\n")
+       }
+
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
+
+       invalidServerCheck := tc.ServercheckRequestNullable{
+               Name:     util.StrPtr("BOGUS"),
+               Value:    util.IntPtr(1),
+               ID:       util.IntPtr(-1),
+               HostName: util.StrPtr("bogus_hostname"),
+       }
+
+       // Attempt to create a ServerCheck with invalid server ID
+       _, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+       if err == nil {
+               t.Errorf("expected to receive error with invalid id\n")
+       }
+
+       invalidServerCheck.ID = nil
+       // Attempt to create a ServerCheck with invalid host name
+       _, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+       if err == nil {
+               t.Errorf("expected to receive error with invalid host name\n")
+       }
+
+       // get valid name to get past host check
+       invalidServerCheck.Name = testData.Serverchecks[0].Name
+
+       // Attempt to create a ServerCheck with invalid servercheck name
+       _, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+       if err == nil {
+               t.Errorf("expected to receive error with invalid servercheck 
name\n")
+       }
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
+}
+
+func UpdateTestServerChecks(t *testing.T) {
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
+       for _, servercheck := range testData.Serverchecks {
+               *servercheck.Value--
+               resp, _, err := TOSession.InsertServerCheckStatus(servercheck)
+               log.Debugf("Response: %v host_name %v check %v", 
*servercheck.HostName, *servercheck.Name, resp)
+               if err != nil {
+                       t.Errorf("could not update servercheck: %v\n", err)
+               }
+       }
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
+}
+
+func GetTestServerChecks(t *testing.T) {
+       hostname := testData.Serverchecks[0].HostName
+       // Get server checks
+       serverChecksResp, _, err := TOSession.GetServerChecks()
+       if err != nil {
+               t.Fatalf("could not GET serverchecks: %v\n", err)
+       }
+       found := false
+       for _, sc := range serverChecksResp.Response {
+               if sc.HostName == *hostname {
+                       found = true
+                       if sc.Checks.ORT != 12 {
+                               t.Errorf("%v returned for ORT value servercheck 
- expected 12\n", sc.Checks.ORT)
+                       }
+
+                       if sc.Checks.ILO != 0 {
+                               t.Errorf("%v returned for ILO value servercheck 
- expected 1\n", sc.Checks.ILO)
+                       }
+                       break
+               }
+       }
+       if !found {
+               t.Errorf("expected to find servercheck for host %v\n", hostname)
+       }
+}
+
+// Need to define no-op function as TCObj interface expects a delete function
+// There is no delete path for serverchecks
+func DeleteTestServerChecks(t *testing.T) {
+       return
+}
diff --git a/traffic_ops/testing/api/v14/tc-fixtures.json 
b/traffic_ops/testing/api/v14/tc-fixtures.json
index 740059c..510a08f 100644
--- a/traffic_ops/testing/api/v14/tc-fixtures.json
+++ b/traffic_ops/testing/api/v14/tc-fixtures.json
@@ -2147,5 +2147,39 @@
                 "regex": "/foo"
             }
         }
+    ],
+    "to_extensions": [
+        {
+            "name": "ILO_PING",
+            "version": "1.0.0",
+            "info_url": "-",
+            "script_file": "ToPingCheck.pl",
+            "isactive": 1,
+            "description": "",
+            "servercheck_short_name": "ILO",
+            "type": "CHECK_EXTENSION_BOOL"
+        },
+        {
+            "name": "ORT_ERROR_COUNT",
+            "version": "1.0.0",
+            "info_url": "-",
+            "script_file": "ToORTCheck.pl",
+            "isactive": 1,
+            "description": "",
+            "servercheck_short_name": "ORT",
+            "type": "CHECK_EXTENSION_NUM"
+        }
+    ],
+    "serverchecks": [
+        {
+            "servercheck_short_name": "ILO",
+            "host_name": "atlanta-edge-01",
+            "value": 1
+        },
+        {
+            "servercheck_short_name": "ORT",
+            "host_name": "atlanta-edge-01",
+            "value": 13
+        }
     ]
 }
diff --git a/traffic_ops/testing/api/v14/todb.go 
b/traffic_ops/testing/api/v14/todb.go
index 9a6fc97..823a15b 100644
--- a/traffic_ops/testing/api/v14/todb.go
+++ b/traffic_ops/testing/api/v14/todb.go
@@ -91,6 +91,18 @@ func SetupTestData(*sql.DB) error {
                os.Exit(1)
        }
 
+       err = SetupTypes(db)
+       if err != nil {
+               fmt.Printf("\nError setting up types %s - %s, %v\n", 
Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
+               os.Exit(1)
+       }
+
+       err = SetupToExtensions(db)
+       if err != nil {
+               fmt.Printf("\nError setting up to_extensions %s - %s, %v\n", 
Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err)
+               os.Exit(1)
+       }
+
        return err
 }
 
@@ -155,6 +167,7 @@ INSERT INTO tm_user (username, local_passwd, role, 
tenant_id) VALUES ('` + Confi
 INSERT INTO tm_user (username, local_passwd, role, tenant_id) VALUES ('` + 
Config.TrafficOps.Users.Admin + `','` + encryptedPassword + `', 4, 1);
 INSERT INTO tm_user (username, local_passwd, role, tenant_id) VALUES ('` + 
Config.TrafficOps.Users.Portal + `','` + encryptedPassword + `', 5, 1);
 INSERT INTO tm_user (username, local_passwd, role, tenant_id) VALUES ('` + 
Config.TrafficOps.Users.Federation + `','` + encryptedPassword + `', 6, 1);
+INSERT INTO tm_user (username, local_passwd, role, tenant_id) VALUES ('` + 
Config.TrafficOps.Users.Extension + `','` + encryptedPassword + `', 3, 1);
 `
        err = execSQL(db, sqlStmt, "tm_user")
        if err != nil {
@@ -233,6 +246,35 @@ INSERT INTO job (id, agent, object_type, object_name, 
keyword, parameters, asset
        return nil
 }
 
+// SetupTypes Set up to_extension types
+func SetupTypes(db *sql.DB) error {
+
+       sqlStmt := `
+INSERT INTO type (id, name, description, use_in_table) VALUES (1, 
'CHECK_EXTENSION_BOOL', 'Extension for checkmark in Server Check', 
'to_extension');
+INSERT INTO type (id, name, description, use_in_table) VALUES (2, 
'CHECK_EXTENSION_NUM', 'Extension for int value in Server Check', 
'to_extension');
+INSERT INTO type (id, name, description, use_in_table) VALUES (3, 
'CHECK_EXTENSION_OPEN_SLOT', 'Open slot for check in Server Status', 
'to_extension');
+`
+       err := execSQL(db, sqlStmt, "type")
+       if err != nil {
+               return fmt.Errorf("exec failed %v", err)
+       }
+       return nil
+}
+
+// SetupToExtensions setup open slot in to_extension table
+func SetupToExtensions(db *sql.DB) error {
+
+       sqlStmt := `
+INSERT INTO to_extension (name, version, info_url, isactive, script_file, 
servercheck_column_name, type) VALUES ('OPEN', '1.0.0', '-', false, '', 'aa', 
3);
+INSERT INTO to_extension (name, version, info_url, isactive, script_file, 
servercheck_column_name, type) VALUES ('OPEN', '1.0.0', '-', false, '', 'ab', 
3);
+       `
+       err := execSQL(db, sqlStmt, "to_extension")
+       if err != nil {
+               return fmt.Errorf("exec failed %v", err)
+       }
+       return nil
+}
+
 // Teardown - ensures that the data is cleaned up for a fresh run
 func Teardown(db *sql.DB) error {
 
diff --git a/traffic_ops/testing/api/v14/toextension_test.go 
b/traffic_ops/testing/api/v14/toextension_test.go
new file mode 100644
index 0000000..8be3df3
--- /dev/null
+++ b/traffic_ops/testing/api/v14/toextension_test.go
@@ -0,0 +1,98 @@
+package v14
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+       "testing"
+       "time"
+
+       "github.com/apache/trafficcontrol/lib/go-log"
+)
+
+var (
+       toReqTimeout = time.Second * 
time.Duration(Config.Default.Session.TimeoutInSecs)
+)
+
+func TestTOExtensions(t *testing.T) {
+       WithObjs(t, []TCObj{TOExtensions}, func() {
+               CreateTestInvalidTOExtensions(t)
+       })
+}
+
+func CreateTestTOExtensions(t *testing.T) {
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
+
+       for _, ext := range testData.TOExtensions {
+               resp, _, err := TOSession.CreateTOExtension(ext)
+               log.Debugf("Response: %v %v", *ext.Name, resp)
+               if err != nil {
+                       t.Errorf("could not create to_extension %v: %v\n", 
ext.Name, err)
+               }
+       }
+
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
+}
+
+func CreateTestInvalidTOExtensions(t *testing.T) {
+       // Fail Attempt to Create ToExtension as non extension user
+       _, _, err := TOSession.CreateTOExtension(testData.TOExtensions[0])
+       if err == nil {
+               t.Errorf("expected to receive error with non extension user\n")
+       }
+}
+
+func DeleteTestTOExtensions(t *testing.T) {
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword)
+
+       extensions, _, err := TOSession.GetTOExtensions()
+       if err != nil {
+               t.Errorf("could not get to_extensions: %v\n", err)
+       }
+       if len(extensions.Response) != len(testData.TOExtensions) {
+               t.Errorf("%v to_extensions returned - expected %v\n", 
len(extensions.Response), len(testData.TOExtensions))
+       }
+
+       ids := []int{}
+       for _, ext := range testData.TOExtensions {
+               found := false
+               for _, respTOExt := range extensions.Response {
+                       if *ext.Name == *respTOExt.Name {
+                               ids = append(ids, *respTOExt.ID)
+                               found = true
+                               continue
+                       }
+               }
+               if !found {
+                       t.Errorf("expected to find to_extension %v\n", 
*ext.Name)
+               }
+       }
+
+       for _, id := range ids {
+               resp, _, err := TOSession.DeleteTOExtension(id)
+               log.Debugf("Response: %v %v", id, resp)
+               if err != nil {
+                       t.Errorf("cannot delete to_extension: %v - %v\n", id, 
err)
+               }
+       }
+       extensions, _, err = TOSession.GetTOExtensions()
+       if err != nil {
+               t.Errorf("could not get to_extensions: %v\n", err)
+       }
+       if len(extensions.Response) != 0 {
+               t.Errorf("%v to_extensions returned - expected %v\n", 
len(extensions.Response), 0)
+       }
+       SwitchSession(toReqTimeout, Config.TrafficOps.URL, 
Config.TrafficOps.Users.Extension, Config.TrafficOps.UserPassword, 
Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword)
+}
diff --git a/traffic_ops/testing/api/v14/traffic_control.go 
b/traffic_ops/testing/api/v14/traffic_control.go
index 2bb195f..d16c755 100644
--- a/traffic_ops/testing/api/v14/traffic_control.go
+++ b/traffic_ops/testing/api/v14/traffic_control.go
@@ -42,8 +42,10 @@ type TrafficControl struct {
        Statuses                       []tc.StatusNullable                
`json:"statuses"`
        StaticDNSEntries               []tc.StaticDNSEntry                
`json:"staticdnsentries"`
        Tenants                        []tc.Tenant                        
`json:"tenants"`
+       TOExtensions                   []tc.TOExtensionNullable           
`json:"to_extensions"`
        Types                          []tc.Type                          
`json:"types"`
        SteeringTargets                []tc.SteeringTargetNullable        
`json:"steeringTargets"`
+       Serverchecks                   []tc.ServercheckRequestNullable    
`json:"serverchecks"`
        Users                          []tc.User                          
`json:"users"`
        Jobs                           []JobRequest                       
`json:"jobs"`
 }
diff --git a/traffic_ops/testing/api/v14/withobjs.go 
b/traffic_ops/testing/api/v14/withobjs.go
index 14e24f4..0363fb6 100644
--- a/traffic_ops/testing/api/v14/withobjs.go
+++ b/traffic_ops/testing/api/v14/withobjs.go
@@ -52,11 +52,13 @@ const (
        ProfileParameters
        Regions
        Roles
+       ServerChecks
        Servers
        Statuses
        StaticDNSEntries
        SteeringTargets
        Tenants
+       TOExtensions
        Types
        Users
        UsersDeliveryServices
@@ -84,11 +86,13 @@ var withFuncs = map[TCObj]TCObjFuncs{
        ProfileParameters:              {CreateTestProfileParameters, 
DeleteTestProfileParameters},
        Regions:                        {CreateTestRegions, DeleteTestRegions},
        Roles:                          {CreateTestRoles, DeleteTestRoles},
+       ServerChecks:                   {CreateTestServerChecks, 
DeleteTestServerChecks},
        Servers:                        {CreateTestServers, DeleteTestServers},
        Statuses:                       {CreateTestStatuses, 
DeleteTestStatuses},
        StaticDNSEntries:               {CreateTestStaticDNSEntries, 
DeleteTestStaticDNSEntries},
        SteeringTargets:                {SetupSteeringTargets, 
DeleteTestSteeringTargets},
        Tenants:                        {CreateTestTenants, DeleteTestTenants},
+       TOExtensions:                   {CreateTestTOExtensions, 
DeleteTestTOExtensions},
        Types:                          {CreateTestTypes, DeleteTestTypes},
        Users:                          {CreateTestUsers, ForceDeleteTestUsers},
        UsersDeliveryServices:          {CreateTestUsersDeliveryServices, 
DeleteTestUsersDeliveryServices},
diff --git a/traffic_ops/traffic_ops_golang/ats/db.go 
b/traffic_ops/traffic_ops_golang/ats/db.go
index 69e5d56..efac0b9 100644
--- a/traffic_ops/traffic_ops_golang/ats/db.go
+++ b/traffic_ops/traffic_ops_golang/ats/db.go
@@ -563,7 +563,7 @@ func GetServerNameFromID(tx *sql.Tx, id int) (tc.CacheName, 
bool, error) {
 // Returns the name, any user error, any system error, and any error code.
 func GetServerNameFromNameOrID(tx *sql.Tx, serverNameOrID string) 
(tc.CacheName, error, error, int) {
        if serverID, err := strconv.Atoi(serverNameOrID); err == nil {
-               serverName, ok, err := dbhelpers.GetServerNameFromID(tx, 
int64(serverID))
+               serverName, ok, err := dbhelpers.GetServerNameFromID(tx, 
serverID)
                if err != nil {
                        return "", nil, fmt.Errorf("getting server name from id 
%v: %v", serverID, err), http.StatusInternalServerError
                } else if !ok {
@@ -573,7 +573,7 @@ func GetServerNameFromNameOrID(tx *sql.Tx, serverNameOrID 
string) (tc.CacheName,
        }
 
        serverName := tc.CacheName(serverNameOrID)
-       if ok, err := dbhelpers.ServerExists(string(serverName), tx); err != 
nil {
+       if _, ok, err := dbhelpers.GetServerIDFromName(string(serverName), tx); 
err != nil {
                return "", nil, fmt.Errorf("checking server name '%v' 
existence: %v", serverName, err), http.StatusInternalServerError
        } else if !ok {
                return "", errors.New("server not found"), nil, 
http.StatusNotFound
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go 
b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index a3b82c7..19b482b 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -274,19 +274,19 @@ func GetCDNDomainFromName(tx *sql.Tx, cdnName tc.CDNName) 
(string, bool, error)
        return domain, true, nil
 }
 
-// ServerExists returns true if the server exists.
-func ServerExists(serverName string, tx *sql.Tx) (bool, error) {
+// GetServerIDFromName gets server id from a given name
+func GetServerIDFromName(serverName string, tx *sql.Tx) (int, bool, error) {
        id := 0
        if err := tx.QueryRow(`SELECT id FROM server WHERE host_name = $1`, 
serverName).Scan(&id); err != nil {
                if err == sql.ErrNoRows {
-                       return false, nil
+                       return id, false, nil
                }
-               return false, errors.New("querying server name: " + err.Error())
+               return id, false, errors.New("querying server name: " + 
err.Error())
        }
-       return true, nil
+       return id, true, nil
 }
 
-func GetServerNameFromID(tx *sql.Tx, id int64) (string, bool, error) {
+func GetServerNameFromID(tx *sql.Tx, id int) (string, bool, error) {
        name := ""
        if err := tx.QueryRow(`SELECT host_name FROM server WHERE id = $1`, 
id).Scan(&name); err != nil {
                if err == sql.ErrNoRows {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
index 6581eaa..0b627e0 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
@@ -54,7 +54,7 @@ func Delete(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       serverName, exists, err := dbhelpers.GetServerNameFromID(inf.Tx.Tx, 
int64(serverID))
+       serverName, exists, err := dbhelpers.GetServerNameFromID(inf.Tx.Tx, 
serverID)
        if err != nil {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("getting server name from id: "+err.Error()))
                return
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go 
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 57bd354..06b7be2 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -68,6 +68,7 @@ import (
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/region"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/role"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/server"
+       
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/servercheck"
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/staticdnsentry"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/status"
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/steering"
@@ -245,10 +246,13 @@ func Routes(d ServerData) ([]Route, []RawRoute, 
http.Handler, error) {
                {1.1, http.MethodGet, `deliveryservice_matches/?(\.json)?$`, 
deliveryservice.GetMatches, auth.PrivLevelReadOnly, Authenticated, nil},
 
                //Server
-               {1.1, http.MethodGet, `servers/checks$`, 
handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
                {1.1, http.MethodGet, `servers/status$`, 
handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
                {1.1, http.MethodGet, `servers/totals$`, 
handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
 
+               //Serverchecks
+               {1.1, http.MethodGet, `servers/checks$`, 
handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
+               {1.1, http.MethodPost, `servercheck/?(\.json)?$`, 
servercheck.CreateUpdateServercheck, auth.PrivLevelInvalid, Authenticated, nil},
+
                //Server Details
                {1.1, http.MethodGet, `servers/details/?(\.json)?$`, 
server.GetDetailParamHandler, auth.PrivLevelReadOnly, Authenticated, nil},
                {1.1, http.MethodGet, 
`servers/hostname/{hostName}/details/?(\.json)?$`, server.GetDetailHandler, 
auth.PrivLevelReadOnly, Authenticated, nil},
diff --git a/traffic_ops/traffic_ops_golang/server/servers_assignment.go 
b/traffic_ops/traffic_ops_golang/server/servers_assignment.go
index fa42e10..b719eec 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_assignment.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_assignment.go
@@ -62,7 +62,7 @@ func AssignDeliveryServicesToServerHandler(w 
http.ResponseWriter, r *http.Reques
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
                return
        }
-       serverName, ok, err := dbhelpers.GetServerNameFromID(inf.Tx.Tx, 
int64(server))
+       serverName, ok, err := dbhelpers.GetServerNameFromID(inf.Tx.Tx, server)
        if err != nil {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("getting server name from ID: "+err.Error()))
                return
diff --git a/traffic_ops/traffic_ops_golang/servercheck/servercheck.go 
b/traffic_ops/traffic_ops_golang/servercheck/servercheck.go
new file mode 100644
index 0000000..fe93f8f
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/servercheck/servercheck.go
@@ -0,0 +1,130 @@
+package servercheck
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+       "database/sql"
+       "errors"
+       "fmt"
+       "net/http"
+
+       
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
+       "github.com/apache/trafficcontrol/lib/go-log"
+       "github.com/apache/trafficcontrol/lib/go-tc"
+
+       "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+)
+
+// CreateUpdateServercheck handles creating or updating an existing servercheck
+func CreateUpdateServercheck(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()
+
+       if inf.User.UserName != "extension" {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusForbidden, 
errors.New("invalid user for this API. Only the \"extension\" user can use 
this"), nil)
+               return
+       }
+
+       serverCheckReq := tc.ServercheckRequestNullable{}
+
+       if err := api.Parse(r.Body, inf.Tx.Tx, &serverCheckReq); err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
+               return
+       }
+
+       id, exists, err := getServerID(serverCheckReq.ID, 
serverCheckReq.HostName, inf.Tx.Tx)
+       if err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("getting server id: "+err.Error()))
+               return
+       }
+       if !exists {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
fmt.Errorf("Server not found"), nil)
+               return
+       }
+
+       col, exists, err := getColName(serverCheckReq.Name, inf.Tx.Tx)
+       if err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("getting servercheck column name: "+err.Error()))
+               return
+       }
+       if !exists {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
fmt.Errorf("Server Check Extension %v not found - Do you need to install it?", 
*serverCheckReq.Name), nil)
+               return
+       }
+
+       err = createUpdateServerCheck(id, col, *serverCheckReq.Value, inf.Tx.Tx)
+       if err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("updating servercheck: "+err.Error()))
+               return
+       }
+
+       successMsg := "Server Check was successfully updated"
+       api.CreateChangeLogRawTx(api.ApiChange, successMsg, inf.User, inf.Tx.Tx)
+       api.WriteRespAlert(w, r, tc.SuccessLevel, successMsg)
+}
+
+func getServerID(id *int, hostname *string, tx *sql.Tx) (int, bool, error) {
+       if id != nil {
+               _, exists, err := dbhelpers.GetServerNameFromID(tx, *id)
+               return *id, exists, err
+       }
+       sID, exists, err := dbhelpers.GetServerIDFromName(*hostname, tx)
+       return sID, exists, err
+}
+
+func getColName(shortName *string, tx *sql.Tx) (string, bool, error) {
+       col := ""
+       log.Infoln(*shortName)
+       if err := tx.QueryRow(`SELECT servercheck_column_name FROM to_extension 
WHERE servercheck_short_name = $1`, *shortName).Scan(&col); err != nil {
+               if err == sql.ErrNoRows {
+                       return "", false, nil
+               }
+               return "", false, errors.New("querying servercheck column name: 
" + err.Error())
+       }
+       return col, true, nil
+}
+
+func createUpdateServerCheck(sid int, colName string, value int, tx *sql.Tx) 
error {
+
+       insertUpdateQuery := fmt.Sprintf(`
+INSERT INTO servercheck (server, %[1]s)
+VALUES ($1, $2) 
+ON CONFLICT (server) 
+DO UPDATE SET %[1]s = EXCLUDED.%[1]s`, colName)
+
+       result, err := tx.Exec(insertUpdateQuery, sid, value)
+       if err != nil {
+               return errors.New("insert server check: " + err.Error())
+       }
+       rowsAffected, err := result.RowsAffected()
+       if err != nil {
+               return errors.New("reading rows affected after server check 
insert : " + err.Error())
+       }
+       if rowsAffected == 0 {
+               return errors.New("server check was inserted, no rows were 
affected : " + err.Error())
+       }
+
+       return nil
+}

Reply via email to