Friday, August 21, 2015

GoLang and MSSQL Databases: An Example

(I'll insert the sources for custom_DB_functions.go and sqltest.go at the end of the post.)
(Addendum - I recently ran into an issue with the query of the database in the custom_DB_functions.go package where calls returned an invalid object. It looks like you have to call it with the schema as well [mydatabase.myschema.mytable] to get the call to work. See this StackOverflow question for details.)

I created a small utility recently that pulled data from a text file to display on a web page. I showed it to my manager, and he said we might be able to integrate it with a more useful bit of our infrastructure already in place, but in order to do that, it would have to talk to an MSSQL server for content instead of the text file.

I'd never really worked with testing that setup. In searching the Internet for examples, there are lots of fragments providing hints how to do it...but nothing really spelled out an example in tutorial form. So I kept notes and now I'm sharing what I learned for other beginners that want to experiment with integrating an MSSQL database with their Go application.

I'm only going to hit some highlights in the source; the source code has quite a bit of commenting and is pretty self-documented (but if you have questions...or suggestions/corrections...please leave a comment! In the blog comments, that is.)

Creating a Test Database Server

 I wanted to set up an accessible test environment for anyone, not just people who happen to have a full-on SQL Server available. I fired up my Windows VM and downloaded MS SQL Server Express 2014. Free for most purposes!

I ran the installer (the 64 bit version with tools) keeping the defaults.

I had to enable network access to the database engine.

  • Open the SQL Server 2014 Configuration Manager
  • Click SQL Server Network Configuration in the left hand column
  • Open Protocols for SQLEXPRESS
    • Make sure TCP/IP is "enabled"
    • Right click TCP/IP, click Properties
    • Click the IP Addresses tab
    • Check that IP2 is in the local subnet
    • Check that TCP Dynamic Ports is blank in the IPALL section
    • Check that the TCP Port is set to 1433
  • Restart the SQL Server (SQLEXPRESS) service
At this point a quick NMap scan of the VM showed that port 1433 was open (when I used the -Pn flag.)

There's another setting to change coming up...

Prepare Your Go Project to Talk to The SQL Server

Go projects talking to a SQL server need two components; a driver, and a library that abstracts that driver from the programmer. The driver depends on what type of server you're talking to, but the abstraction layer is pretty standard.

Since we're talking to MSSQL, we'll use the go-mssqldb driver. From your Go workspace run:

go get github.com/denisenkom/go-mssqldb

I decided I was going to create a test application that imported a custom package of functions that specifically accessed the database; that way I could import the resulting package to my existing application and make necessary alterations at that point.

In the package I imported the driver with the line

_ "github.com/denisenkom/go-mssqldb"

WHAT IS THAT UNDERSCORE?

The problem is that I don't use the package identifier for anything in the program; when I tried to compile it, the compiler will error. What I really needed from the driver was the initialization method; the underscore will run the init() function but ignore everything else, and the compiler won't complain.

The abstraction part...the generic SQL Go functions to interact with the database...are imported with 

"database/sql"

That import is in my package (custom_DB_functions.go and sqltest.go.) The driver is only imported in the package.

The test program was literally a "add functions as I go along and verify they work" thing. Add a function, code it in the package, recompile...repeat until I had most of the functions I wanted to work. 

In order to make the test program flexible, I imported the flag package and created entries for connecting to the database from the command line. Import with

import (
     "flag"
)

In main(), I added a series of flags using 


// Flags
ptrVersion := flag.Bool("version", false, "Display program version")
ptrDeleteIt := flag.Bool("deletedb", false, "Delete the database")
ptrServer := flag.String("server", "localhost", "Server to connect to")
ptrUser := flag.String("username", "testuser", "Username for authenticating to database; if you use a backslash, it must be escaped or in quotes")
ptrPass := flag.String("password", "", "Password for database connection")
ptrDBName := flag.String("dbname", "test_db", "Database name")

flag.Parse()

You can probably divine the meaning from the syntax, but these are in the form of

<variable> := flag.<type>("flagname", default setting if not provided at command line, "Help explanation")

The variable created is a pointer, and flag.Parse() evaluates the flags at runtime. The default values are set to the second argument if they're not changed at the command line, so you can use them as variables without having to set them to something before referring to them.

The first function I wanted to test was the creation of a database handle. It seems that the behavior of Open relies on the underlying driver; it may, as the docs say, validate arguments without a connection to the database, so it may be necessary to actually do something with the handle before active connections are made. At any rate, here's the function in sqltest.go:

db, err := sql.Open("mssql", "server="+*ptrServer+";user id="+*ptrUser+";password="+*ptrPass)
if err != nil {
fmt.Println("From Open() attempt: " + err.Error())
}
 defer db.Close()

Now db is a handle to the database. Because of the way database connections are handled, you don't want to keep opening and closing them. Just defer the Close() until the program exits and that way you won't get goofy pooling/caching issues.

From what I can tell of the documentation the db handle must be kept open for the length of time that you're using it (don't close it until the program exits.) This lets the driver manage a pool of connections; when you operate on the database you use a connection from the pool. Those connection(s) you'll want to close so the connection gets returned to the connection pool. In this program I deferred the close of the db handle because the scope of the test program should keep it open until sqltest ends.

Basically Open() needs the type of database (MSSQL), the server IP or DNS name, the username, and password. I should also note that the username, if you're using Windows auth, the backslash must be entered twice so it's escaped properly or the username must be encased in quotes. for example, the connection at the command line might look like:

./sqltest -password=HelloThere -server=192.168.254.222 -username=MySystem\\testuser

Notice there's two backslashes in the username?

Skipping ahead a little, sqltest.go makes a call to PingServer(). In the package, PingServer looks like:

func PingServer(db *sql.DB) string {

err := db.Ping()
if err != nil {
return ("From Ping() Attempt: " + err.Error())
}

return ("Database Ping Worked...")

}

It's a rather simple function, and all it does is run a call to db.Ping and returns a string with an error or an affirmation that it's working. This created a working connection to the database (as discussed before), and also tested the ability to pass the database handle to a package function.

Something else to note in sqltest.go is that I called the initial Open() using sql.Open(), while the call to PingServer was simply PingServer(). The trick to that is in the import statement. 

import (
"database/sql"
"flag"
"fmt"
"os"
. "custom_DB_functions"
"strconv"
)

The import for "custom_DB_functions" is preceded by a period. That allows me to refer to the functions without the preceding library name; if I preceded "fmt" with a period I should be able to use lines like Println("I'm printing this to the console!") instead of fmt.Println("I'm printing this to the console!"). I don't know what happens if I had multiple functions with the same name in different packages imported with the period...I would think the compiler would insist on the proper namespace for referencing those specific cases, but I haven't tested it.

Skipping ahead a little more there's a spot where I create the database if it doesn't already exist:

// If it doesn't exist, let's create the base database
if !boolDBExist {

CreateDBAndTable(db, *ptrDBName)
fmt.Println("********************************")

}

In the library, the call looks like this:

func CreateDBAndTable(db *sql.DB, strDBName string) error {

// Create the database
_, err := db.Exec("CREATE DATABASE [" + strDBName + "]")
if err != nil {
return (err)
}

// Let's turn off AutoClose
_, err = db.Exec("ALTER DATABASE [" + strDBName + "] SET AUTO_CLOSE OFF;")
if err != nil {
return (err)
}

// Create the tables
_, err = db.Exec("USE " + strDBName + "; CREATE TABLE testtable (source nvarchar(100) NOT NULL, timestamp bigint NOT NULL, content nvarchar(4000) NOT NULL)")
if err != nil {
return (err)
}

return nil

}

It was here that I encountered an error from the database. 

Next Database Configuration Change: "CREATE DATABASE permission denied in database 'master'"

The actual SQL calls aren't all that difficult if you already understand SQL (the big notes involve not constantly opening and closing the database handle, and to use Query() when getting information back to process and Exec() when the reply is more or less either "this worked" or "error!" 

In the above calls, you can see that the function creates a database, alters the AUTO_CLOSE setting on the database so it doesn't throw goofball errors when trying later queries, and then creates a table with particular attributes. 

What I got back the first time was the CREATE DATABASE permission denied in database 'master' error. That required some more tinkering with the database engine.
  • Open SQL Server 2014 Management Studio and connect to the test database
    • Click on your SQL EXPRESS instance
    • Expand the Security folder
    • Expand the Logins folder
    • I created a local user on my system for testing, so I right clicked on BUILTIN\Users and select Properties
    • Click on "Server Roles" on the left side
    • Select "dbcreator" from the list of roles; this should be enough access
Clicking okay and re-running the database/table creation calls should work. CreateDBAndTable() and DropDB() were created mostly for testing purposes so I could periodically work with a fresh database without having to futz with Management Studio or other interface (and of course I learned how to do these tasks programmatically.)

Any Other Notes?

Between heavy commenting and naming functions and variables in a (hopefully) mostly obvious manner I think that the code is semi-obvious; most of what I would explain in text here would seem redundant or obvious. If you have questions feel free to leave a blog comment!

The only thing that comes to mind from the design point of view is the use of returning an error from the library functions instead of dumping something to the console. This means the caller is responsible for the presentation of messages; the application can decide if the returned message goes to the console or redirected to a file or possibly ignored.

Here's the source code! Hope it is somewhat helpful to someone! (Probably it will be most useful to me as a reference while trying to tune what I've been working on...)

Source Code

custom_DB_functions.go 

// Package custom_DB_functions contains functions customized to manipulate MSSQL databases/tables
// for our application
//
// Version 0.15, 8-13-2015
package custom_DB_functions

import (
 "database/sql"
 _ "github.com/denisenkom/go-mssqldb"
 "strconv"
)

// PingServer uses a passed database handle to check if the database server works
func PingServer(db *sql.DB) string {

 err := db.Ping()
 if err != nil {
  return ("From Ping() Attempt: " + err.Error())
 }

 return ("Database Ping Worked...")

}

// CheckDB checks if the database "strDBName" exists on the MSSQL database engine.
func CheckDB(db *sql.DB, strDBName string) (bool, error) {

 // Does the database exist?
 result, err := db.Query("SELECT db_id('" + strDBName + "')")
 defer result.Close()
 if err != nil {
  return false, err
 }

 for result.Next() {
  var s sql.NullString
  err := result.Scan(&s)
  if err != nil {
   return false, err
  }

  // Check result
  if s.Valid {
   return true, nil
  } else {
   return false, nil
  }
 }

 // This return() should never be hit...
 return false, err
}

// CreateDBAndTable creates a new content database on the SQL Server along with
// the necessary tables. Keep in mind the user credentials that opened the database
// connection with sql.Open must have at least dbcreator rights to the database. The
// table (testtable) will have columns source (nvarchar), timestamp (bigint), and
// content (nvarchar).
func CreateDBAndTable(db *sql.DB, strDBName string) error {

 // Create the database
 _, err := db.Exec("CREATE DATABASE [" + strDBName + "]")
 if err != nil {
  return (err)
 }

 // Let's turn off AutoClose
 _, err = db.Exec("ALTER DATABASE [" + strDBName + "] SET AUTO_CLOSE OFF;")
 if err != nil {
  return (err)
 }

 // Create the tables
 _, err = db.Exec("USE " + strDBName + "; CREATE TABLE testtable (source nvarchar(100) NOT NULL, timestamp bigint NOT NULL, content nvarchar(4000) NOT NULL)")
 if err != nil {
  return (err)
 }

 return nil

}

// DropDB deletes the database strDBName.
func DropDB(db *sql.DB, strDBName string) error {

 // Drop the database
 _, err := db.Exec("DROP DATABASE [" + strDBName + "]")

 if err != nil {
  return err
 }

 return nil

}

// AddToContent adds new content to the database.
func AddToContent(db *sql.DB, strDBName string, strSource string, int64Timestamp int64, strContent string) error {

 // Add a record entry
 _, err := db.Exec("USE " + strDBName + "; INSERT INTO testtable (source, timestamp, content) VALUES ('" + strSource + "','" + strconv.FormatInt(int64Timestamp, 10) + "','" + strContent + "');")
 if err != nil {
  return err
 }

 return nil

}

// RemoveFromContentBySource removes a record from the database with source strSource. The
// int64 returned is a message indicating the number of rows affected.
func RemoveFromContentBySource(db *sql.DB, strSource string) (int64, error) {

 // Remove entries containing the source...
 result, err := db.Exec("DELETE FROM testtable WHERE source=$1;", strSource)
 if err != nil {
  return 0, err
 }

 // What was the result?
 rowsAffected, _ := result.RowsAffected()
 return rowsAffected, nil

}

// Query the content in the database and return the source (string), timestamp (int64), and
// content (string) as slices
func GetContent(db *sql.DB) ([]string, []int64, []string, error) {

 var slcstrContent []string
 var slcint64Timestamp []int64
 var slcstrSource []string

 // Run the query
 rows, err := db.Query("SELECT source, timestamp, content FROM testtable")
 if err != nil {
  return slcstrSource, slcint64Timestamp, slcstrContent, err
 }
 defer rows.Close()

 for rows.Next() {

  // Holding variables for the content in the columns
  var source, content string
  var timestamp int64

  // Get the results of the query
  err := rows.Scan(&source, &timestamp, &content)
  if err != nil {
   return slcstrSource, slcint64Timestamp, slcstrContent, err
  }

  // Append them into the slices that will eventually be returned to the caller
  slcstrSource = append(slcstrSource, source)
  slcstrContent = append(slcstrContent, content)
  slcint64Timestamp = append(slcint64Timestamp, timestamp)
 }

 return slcstrSource, slcint64Timestamp, slcstrContent, nil

}
sqltest.go 
package main

// Notice in the import list there's one package prefaced by a ".",
// which allows referencing functions in that package without naming the library in
// the call (if using . "fmt", I can call Println as Println, not fmt.Println)
import (
 "database/sql"
 "flag"
 "fmt"
 "os"
 . "custom_DB_functions"
 "strconv"
)

const strVERSION string = "0.18 compiled on 8/11/2015"

// sqltest is a small application for demonstrating/testing/learning about SQL database connectivity from Go
func main() {

 // Flags
 ptrVersion := flag.Bool("version", false, "Display program version")
 ptrDeleteIt := flag.Bool("deletedb", false, "Delete the database")
 ptrServer := flag.String("server", "localhost", "Server to connect to")
 ptrUser := flag.String("username", "testuser", "Username for authenticating to database; if you use a backslash, it must be escaped or in quotes")
 ptrPass := flag.String("password", "", "Password for database connection")
 ptrDBName := flag.String("dbname", "test_db", "Database name")

 flag.Parse()

 // Does the user just want the version of the application?
 if *ptrVersion == true {
  fmt.Println("Version " + strVERSION)
  os.Exit(0)
 }

 // Open connection to the database server; this doesn't verify anything until you
 // perform an operation (such as a ping).
 db, err := sql.Open("mssql", "server="+*ptrServer+";user id="+*ptrUser+";password="+*ptrPass)
 if err != nil {
  fmt.Println("From Open() attempt: " + err.Error())
 }

 // When main() is done, this should close the connections
 defer db.Close()

 // Does the user want to delete the database?
 if *ptrDeleteIt == true {
  boolDBExist, err := CheckDB(db, *ptrDBName)
  if err != nil {
   fmt.Println("Error running CheckDB: " + err.Error())
   os.Exit(1)
  }
  if boolDBExist {
   fmt.Println("(sqltest) Deleting database " + *ptrDBName + "...")
   DropDB(db, *ptrDBName)
   os.Exit(0)
  } else {

   // Database doesn't seem to exist...
   fmt.Println("(sqltest) Database " + *ptrDBName + " doesn't appear to exist...")
   os.Exit(1)

  }
 }

 // Let's start the tests...
 fmt.Println("********************************")

 // Is the database running?
 strResult := PingServer(db)
 fmt.Println("(sqltest) Ping of Server Result Was: " + strResult)

 fmt.Println("********************************")

 // Does the database exist?
 boolDBExist, err := CheckDB(db, *ptrDBName)
 if err != nil {
  fmt.Println("(sqltest) Error running CheckDB: " + err.Error())
  os.Exit(1)
 }

 fmt.Println("(sqltest) Database Existence Check: " + strconv.FormatBool(boolDBExist))

 fmt.Println("********************************")

 // If it doesn't exist, let's create the base database
 if !boolDBExist {

  CreateDBAndTable(db, *ptrDBName)
  fmt.Println("********************************")

 }

 // Enter a test record
 boolDBExist, err = CheckDB(db, *ptrDBName)
 if err != nil {
  fmt.Println("(sqltest) CheckDB() error: " + err.Error())
  os.Exit(1)
 }

 if boolDBExist == true {

  err := AddToContent(db, *ptrDBName, "Bob", 1437506592, "Hello!")
  if err != nil {
   fmt.Println("(sqltest) Error adding line to content: " + err.Error())
   os.Exit(1)
  }

  err = AddToContent(db, *ptrDBName, "user", 1437506648, "Now testing memory")
  if err != nil {
   fmt.Println("(sqltest) Error adding line to content: " + err.Error())
   os.Exit(1)
  }

  err = AddToContent(db, *ptrDBName, "user", 1437503394, "test, text!")
  if err != nil {
   fmt.Println("(sqltest) Error adding line to content: " + err.Error())
   os.Exit(1)
  }

  err = AddToContent(db, *ptrDBName, "Bob", 1437506592, "Hope this works!")
  if err != nil {
   fmt.Println("(sqltest) Error adding line to content: " + err.Error())
   os.Exit(1)
  }

 }

 fmt.Println("(sqltest) Completed entering test records.")

 fmt.Println("********************************")

 fmt.Println("(sqltest) Deleting records from a particular source.")

 // Delete from a source
 int64Deleted, err := RemoveFromContentBySource(db, "user")
 if err != nil {
  fmt.Println("(sqltest) Error deleting records by source: " + err.Error())
  os.Exit(1)
 } else {

  // How many records were removed?
  fmt.Println("Removed " + strconv.FormatInt(int64Deleted, 10) + " records")
  fmt.Println("********************************")

 }

 // Get the content
 slcstrSource, slcint64Timestamp, slcstrContent, err := GetContent(db)
 if err != nil {
  fmt.Println("(sqltest) Error getting content: " + err.Error())
 }

 // Now read the contents
 for i := range slcstrContent {

  fmt.Println("Entry " + strconv.Itoa(i) + ": " + strconv.FormatInt(slcint64Timestamp[i], 10) + ", from " + slcstrSource[i] + ": " + slcstrContent[i])

 }

}

Tuesday, August 18, 2015

Adventures in Apartment Mismanagement

I like to look at examples of events and practices that piss me off and question, "How could this be improved? Is there a good reason the system sucks?"

I'm running into that today with my apartment management company.

On a Saturday afternoon not long ago my family and I were sitting in the apartment trying not to melt during yet another hot, sticky NYC day. All of a sudden the doorbell rang. 

The ring was followed shortly by another ring. My wife and I looked at each other, perplexed; we didn't order anything. We weren't expecting anyone from the maintenance department. The way the building was managed, I didn't even try to get deliveries. Who the hell could it be?

Before I could get up, the fucking door unlocked.

Yeah. We keep the door locked and chained when in the apartment. And whoever was at the door unlocked it. The door started to open, only to have the chain go taut.

A 20-something year old guy and his companion peered in, mumbled something about, "Oh, someone's home," and closed the door. 

I grabbed a shirt...like I said, it was hot...and opened the door. The hallway was empty.

I went down to the front desk where "security" is stationed. I explained what happened, and he made a phone call. He said that maintenance didn't have anyone there. He didn't know why anyone would be on our floor. "They shouldn't do that...that's like an invasion of privacy or something."  Yeah, thanks Security. I'm feeling more secure knowing how sure you are of the rights of your tenants not to have intruders barge in. "The super has a master key, and some keys that are good in certain apartments. But access to those are controlled." Well, that's good, I guess. If this was an employee they should know who has access.

He asked if I wanted to file a report. 

"Well...yes." I just had someone with a copy of my key open the door to my apartment. If I wasn't there, would he have stolen my laptop? Would he have used the toilet? Maybe kick back and watch my non-cable-connected TV?

Security said he'd sent the cop up to my apartment, since I said I would head back up there and my wife could help with the description of what happened. After a few minutes the doorbell rang and a uniformed cop...think he has a "special patrol" badge sewn on his uniform?...is standing there. He takes down the description of what happened on a piece of what seemed to be scratch paper plucked from the corner of a desk he passed on his way up. 

We're short on details. Male. No uniform, so he's not maintenance...t-shirt and shorts, my wife said. Early to mid twenties. Maybe 5'9" or so. I wanted to say Asian, but wasn't entirely sure...it happened pretty fast and I'd rather say nothing than give the incorrect details. Second guy standing behind him in the hall. 

The cop says that sometimes lock keys work in multiple apartments. "Someone could have gone into the wrong apartment," he says. "Got on the wrong floor, or something. It happens." I wanted to remind him of the detail regarding the doorbell. I don't know too many people that are like, "Home at last! I'd better ring the doorbell a few times before going in. In case, I don't know, my wife is banging the neighbor. They'd appreciate the heads up that I'm home."

"You should get a top lock," the cop continued. He's referring to getting a deadbolt that the resident installs, instead of just the door lock the super has access to.

We thank him and he leaves. I'm wondering if they're going to follow up using the security footage from the elevators. 

At this point I'm uncomfortable leaving the apartment, since that means the chain would be undone and Heckle and Jeckle might come back. I give my wife some cash and ask her to run to a store to find a lock we could install; she's had experience with lock installation and the door has a pre-drilled cylinder hole in it from another resident in a bygone era. Surely we can figure this out.

She gets the lock, and after an hour of trying...it won't work. The holes already in the door from a previous resident are too large for the screws in the lock kit, and the bolt doesn't readily fit into the frame in a way that allows the door to shut.

I go online and file a maintenance ticket marked "emergency" priority explaining what happened and asking for assistance in getting the lock mounted. I mention that at this point, my son is disturbed at the idea of sleeping in the apartment without periodically checking that the door is chained so "bad men" won't come in during the night.

"Dad, is it okay to stab people that come into the apartment if we don't know them?"

He's such a kidder. But I tell him that yes, he can stab people that break into the apartment.

On Sunday I get a call from the super saying he got an email about the incident. I'm not entirely sure, but I think the super for my apartment building is also the head of maintenance for our building, so...did he hear about this from security? The management office? My maintenance ticket? I'm not sure. We had convinced ourselves that this incident was weird enough that we could go out for awhile, since this was our last weekend together in the city and we'd been promising Lil' Dude we'd take him to Coney Island for a walk on the beach and a trip to Nathan's famous hot dog stand. Plus talking on the phone is not my favorite activity in the world, so making out everything he was saying was taking extra effort. 

What I did get out of the call was that he didn't know who it was, he didn't know of any reason for it to happen, and once again, we should get a top lock. He also promised to send a recommended locksmith contact information to me via email.

Monday morning came and my wife sent a message that the maintenance guy came, only to be told they won't install locks or have anything to do with locks. Considering that we needed this to get done on Tuesday at the latest since I was leaving town...yay, school starting plus doctor appointment/lecture time...this was not news I wanted to hear. We bought the lock. We just didn't have a way to get the damn screws to fit into a door that was already fucked up from a previous tenant.

Well...fuck it.

My wife found a recommended locksmith, available 24 hours, with high ratings on Yelp...we never got a recommended locksmith from the super. My wife insisted I had to contact them ("Your name is on the lease!") and they made an appointment for early afternoon.

True to their word, they arrived, and $160 later, we had a new top-lock. Their lock, plus they drilled into the frame area to get it all fitted properly. My wife was impressed. I was relieved to get this crap out of the way before having to leave.

I can understand why the building would have a policy against installing locks since that can imply some kind of liability if someone still broke in after the fact. At the same time, I haven't seen too much action taken in regards to actually trying to figure out who the fuck opened my door. 

It was most likely not another apartment-dweller who happened to have a key that accidentally fit my apartment lock. As I said...they rang the doorbell and were surprised we were home.

It wasn't building maintenance...they were out of uniform.

They had a key that may likely have been an access key...the super is supposed to have it, or the main office. Someone that has access for building work or in the event of a lockout. Supposedly these keys are locked up, and possession is tracked.

So far all I've heard is a lot of head scratching, like "That's not supposed to happen..." Well, no shit.

I haven't even heard if someone checked the security cameras on the floor. They knew approximately when it happened. I was told no one else reported a disturbance. More to the point, my neighbor has papers sticking out of their door...they're obviously not home since the papers have been there awhile. So...why my place? And why aren't the security cameras being checked for two guys, in that approximate time frame, riding the elevators after I said they had disappeared from the hallways? Are the cameras nonfunctional (I should hope not...little corner dome cams suddenly appeared in the elevators recently...) or are they only consulted if someone is assaulted in the elevator?

The entire feeling I get is one of lackadaisical malaise from people involved. Like, "Well, these things happen. You should get a better lock."

We have "security" at the entrance of the building and the only thing they seem to guard against is me getting a Dominoes delivery (or any other package delivery, for that matter, if it doesn't fit in my mailbox...) Any stranger can walk in if they wait long enough for someone to walk through.

We have cameras, but I still have no indication they're going to check it.

There are supposed to be secured keys with limited access to each of the apartments. But I've heard nothing of them being audited. Does someone have the key that shouldn't? And what about the whole "Tenant keys sometimes fit other doors" thing. That's a bit of a worrying thing, don't you think?

I feel like my car broke down, smoke pouring from the engine, fluids spurting out like a fountain, and the mechanic...the people who should know what they're doing...is standing there, scratching his chin and ruminating, "Well, that ain't supposed to happen..."

No shit. Such is life in the city, apparently. And another reason to think landlords just hate their tenants.

Wednesday, August 5, 2015

Let's Make a Go (Golang) Package!

Go tends to be rigid in certain customs. That isn't to say you can't bend or break these customs, but the culture certain makes it clear that some things are...frowned upon.

For example, you can write fairly complicated programs using just a single .go file. Good coding practice is to put your reusable functions into a library (or in Go, a package).

I'm assuming a file structure similar to this for your workspace:

Go
     |
     |-bin
     |-pkg
     |-src

Now, let's create a small main() Go file.

Go
     |
     |-bin
     |-pkg
     |-src
          |-uses_a_package
               |-uses_a_package.go

I have a personal habit when creating files to test out theories or implementations of using the word "test" in the names. Go language developers frown on this practice because "test" is used as a literal code testing reserved word; one of the things I personally find screwy about the language, but it is what it is. If you put something like "uses_a_package_test.go" as a filename, you're in for some head scratching and Google searches leading you to articles about running application tests.

So in the file /go/src/uses_a_package/uses_a_package.go I put the following:


package main

import (
	"fmt"
	"package_demo"
)

func main() {

	// main() says hello
	fmt.Println("Hello from main()!")

	// Package, say hello
	package_demo.Howdy()

}

Now I need the package. I create a new directory and .go file:

Go
     |
     |-bin
     |-pkg
     |-src
          |-uses_a_package
               |-uses_a_package.go
          |-package_demo
               |-package_demo.go

In package_demo.go I enter the following:



// Package package_demo is demonstrating how to make a simple package file/library
package package_demo

import (
	"fmt"
)

// Howdy() says hello with no arguments and no value returned
func Howdy() {

	// Prints a hello to the console
	fmt.Println("Hello from the package_demo package!")

	return
}

Next I compile the everything. From /go/src/uses_a_package, I run:

go install

I didn't have to separately compile the package. Why?

The workspace now looks like this:


Go
     |
     |-bin
          |-uses_a_package
     |-pkg
          |-darwin_amd64
               |package_demo.a
     |-src
          |-uses_a_package
               |-uses_a_package.go
          |-package_demo
               |-package_demo.go

The answer is when I ran the "install" command, Go saw the extra package import and found that it, too, needed compilation. I can run the install command just on the package and it'll compile and insert it into my /go/pkg directory, but I didn't need to.

Running the application:

../../bin/uses_a_package

...yields the following output:

Hello from main()!

Hello from the package_demo package!

The way I added comments to the package is kind of important. It's another bit of knowledge that isn't necessarily intuitive, but you run across it in trying to get other things working in Go; the self-documenting feature of GoDoc.

From /go/src, I run:

godoc ./package_demo

Output is:

PACKAGE DOCUMENTATION

package package_demo
    import "./package_demo"

    Package package_demo is demonstrating how to make a simple package
    file/library

FUNCTIONS

func Howdy()
    Howdy() says hello with no arguments and no value returned

There is definitely room for improvement in how I documented things with comments, but you get the point. Properly comment your code in Go!

I should also note that GoDoc is used for the documentation on the main Go site. Visit that to get an idea how to properly document your functions and code...as I'm throwing together quick demo stuff for this, you can see I say something stupidly obvious like "no arguments and no return value."

Now, why is GoDoc run on the directory, and not on the file? For one, it doesn't work.

godoc ./package_demo/package_demo.go
2015/07/31 13:44:27 newDirectory(/target): not a package directory

Two, the directory acts like the package, and the files in it are the goodies. Let's try something...

in /go/src/package_demo, I create a new file called package_demo2.go and populate it with the following text:



// Package package_demo2 takes the package demonstration one step farther, showing one package
// directory is "merged" into one package despite multiple sub-files
package package_demo

import (
	"fmt"
)

// CustomHello takes a name passed as a parameter and prints a greeting to that string
func CustomHello(strName string) {

	// Print a custom hello!
	fmt.Println("Hello, " + strName + "!")

	return
}

...and add a call in main() to the added function:



package main

import (
	"fmt"
	"package_demo"
)

func main() {

	// main() says hello
	fmt.Println("Hello from main()!")

	// Package, say hello
	package_demo.Howdy()

	// Second package file, say hello to Bob!
	package_demo.CustomHello("Bob")
}

Compile, and run:

../../bin/uses_a_package
Hello from main()!
Hello from the package_demo package!
Hello, Bob!

At this point the directory structure looks like this:

Go
     |
     |-bin
          |-uses_a_package
     |-pkg
          |-darwin_amd64
               |package_demo.a
     |-src
          |-uses_a_package
               |-uses_a_package.go
          |-package_demo
               |-package_demo.go
               |-package_demo2.go


And of course a function in the package can return results. A quick edit to package_demo.go:


// Package package_demo is demonstrating how to make a simple package file/library
package package_demo

import (
	"fmt"
)

// Howdy() says hello with no arguments and no value returned
func Howdy() {

	// Prints a hello to the console
	fmt.Println("Hello from the package_demo package!")

	return
}

// HelloBack will return a string with a greeting to the caller rather than printing directly to the console
func HelloBack(strName string) string {

	// Who to say hello to?
	strName = "Hello there, " + strName + "!"

	return(strName)

}

...followed by a call added to uses_a_package.go:



package main

import (
	"fmt"
	"package_demo"
)

func main() {

	// main() says hello
	fmt.Println("Hello from main()!")

	// Package, say hello
	package_demo.Howdy()

	// Second package file, say hello to Bob!
	package_demo.CustomHello("Bob")

	// Back to the first file...
	fmt.Println(package_demo.HelloBack("Phil"))

}

Then compile and  test!

go install
../../bin/uses_a_package

Hello from main()!
Hello from the package_demo package!
Hello, Bob!
Hello there, Phil!

And of course GoDoc is helpful with documentation. From go/src, I ran "godoc ./package_demo":

PACKAGE DOCUMENTATION

package package_demo
    import "./package_demo"

    Package package_demo is demonstrating how to make a simple package
    file/library

    Package package_demo2 takes the package demonstration one step farther,
    showing one package directory is "merged" into one package despite
    multiple sub-files

FUNCTIONS

func CustomHello(strName string)
    CustomHello takes a name passed as a parameter and prints a greeting to
    that string

func HelloBack(strName string) string
    HelloBack will return a string with a greeting to the caller rather than
    printing directly to the console

func Howdy()

    Howdy() says hello with no arguments and no value returned

One thing to note is functions to be exported in the package...visible to the executable main()...must be capitalized.  An edit to our growing package_demo.go file:



// Package package_demo is demonstrating how to make a simple package file/library
package package_demo

import (
	"fmt"
)

// Howdy() says hello with no arguments and no value returned
func Howdy() {

	// Prints a hello to the console
	fmt.Println("Hello from the package_demo package!")

	return
}

// HelloBack will return a string with a greeting to the caller rather than printing directly to the console
func HelloBack(strName string) string {

	// Who to say hello to?
	strName = "Hello there, " + strName + "!"

	return(strName)

}

// CallsFuncInPackageSelf takes a name passed as an argument and calls another function within the package
// source, a non-exported (capitalized) function, to construct a reply
func CallsFuncInPackageSelf(strName string) {

	strResponse := constructString(strName)
	fmt.Println(strResponse)

}

// constructString crafts a greeting and returns a reply, but only works within the package
func constructString(strGreetMe string) string {

	return("A secret hello to " + strGreetMe + "!")

}

You'll notice I now added a CallsFuncInPackageSelf function, which when invoked calls "constructString" (lowercase first letter) to return a secret hello to the name that was passed to CallsFuncInPackageSelf. In main() I make a small addition:



package main

import (
	"fmt"
	"package_demo"
)

func main() {

	// main() says hello
	fmt.Println("Hello from main()!")

	// Package, say hello
	package_demo.Howdy()

	// Second package file, say hello to Bob!
	package_demo.CustomHello("Bob")

	// Back to the first file...
	fmt.Println(package_demo.HelloBack("Phil"))

	// Now a secret greeting...
	package_demo.CallsFuncInPackageSelf("Tom")

}
Run "go install" then call the resulting binary and:

../../bin/uses_a_package
Hello from main()!
Hello from the package_demo package!
Hello, Bob!
Hello there, Phil!
A secret hello to Tom!

But if I edit uses_a_package.go to directly call constructString("Tom") and try a recompile, I get this:

go install
# uses_a_package
./uses_a_package.go:26: cannot refer to unexported name package_demo.constructString

./uses_a_package.go:26: undefined: package_demo.constructString

In order for the function to be visible outside the package, it must begin with a capital letter. That's why when you're working with methods like fmt.Println() the function has a capital...in this case, it's exported from the fmt package!

I hope this is helpful in getting started creating packages in Go!

Saturday, August 1, 2015

Create a Remote Git Repository Server

I had covered my first adventures with Git in a previous post. That article focused on creating a local repository; you could save your versions to the local hard drive. That approach works well enough for local work...you can roll back changes, keep track of changes, get frustrated with Git on your local system.

But what if you want to have some redundancy in the case of hard disk failure? Or if you want to work on multiple machines?

In that case you might want to create a simple Git server, a system on which to save your progress and transfer them to other machines. The process isn't all that difficult.

In this example I'm going to turn a Raspberry Pi with a USB-connected hard drive into a network Git server.

Create the Git User on the Server

First, on the Pi, I add a git user.

sudo adduser git
su git
cd
mkdir .ssh
chmod 700 ./.ssh

Raspian on the Pi had an implementation of adduser that asked for the password by default. If yours didn't, you'd have to run "sudo passwd git" after creating the user so you can su into the account. Plus you generally want a password on any account you create on a networked machine...

Enable Passwordless Connection with SSH

Now I'm going to get a public key from the developer (client) machine.

If you have a .id_rsa.pub in your ~/.ssh directory, you're already set. If not, you can usually create one with a variation of:

ssh-keygen -t rsa -C "your_email@address.com"

Then transfer the public key to the git server. ONLY transfer the .pub file!

scp ~/.ssh/id_rsa.pub git@gitserveraddress:~/

Now, back on the Git server:

mv ~/id_rsa.pub /home/git/.ssh
cd /home/git/.ssh

Make sure the .pub file is owned by the git user. If there are no other files in .ssh (which there aren't, if this is the first time you're setting this up...just rename the .pub file to authorized_keys. If the file were already there, I'd append the new .pub file contents to authorized_keys. 

mv id_rsa.pub authorized_keys

Now, if you're on the client computer and run "ssh git@gitmachinesaddress, it should automatically log in.

Time to Create a Workspace to Save Projects

Next step is to create your git workspace. On the Pi, I'm going to put it into my USB-connected drive instead of the SSD card. If you're familiar with Linux, it's not hard to suss out what I'm doing and what you'll have to modify for your own installation. I'm going to exit back to my default user (who already has sudo ability, which git doesn't...) to do the following.

cd /mnt/mydrive
sudo mkdir gitrepo
sudo chown git:git gitrepo

Create a Directory For Your Project (Each Time)

Now create a directory, as git, for your project. In this case, I'm going to use my loopcheck.go test application. As git, create the loopcheck project directory.

su git
cd /mnt/mydrive/gitrepo
mkdir loopcheck.git
cd loopcheck.git
git --bare init

Push the Project to the Server

Almost there! On the development computer, add the remote repo and push it to the server. You did already have the project git-staged, yes?

From the project's source code directory (where you already have git versioning...)

git remote add origin git@gitserveraddress:/mnt/mydrive/gitrepo/loopcheck.git
git push origin master

There you go! Now the loopcheck project is on the local system and a remote git server!

Pull the Project to Another Client

If you want to duplicate your project on another system, you can use the clone command. Most Go projects live under /src. So I would connect to the system I want to work on and (for me) 

cd ./go/src
git clone git@gitserveraddress:/path/to/repo/on/server/loopcheck.git

...at that point I should have the loopcheck project available, and can cd into the project, edit files, and use 

git add .
git commit -m "message for the commit"
git push

...to update the repo! Be aware that if you didn't install the public key from the new workstation, it will prompt you for the git user's password in order to download the project.

There is no tracking information for the current branch...

When I tried pulling the changes from client 2 to client 1, I had an error that began, "There is no tracking information for the current branch..."

A quick fix is to use 

git branch --set-upstream-to=origin/master master

...then it let me use git pull to pull the changes.