How Do You Find Which Docker Images Your Kubernetes Cluster Pods Are Using?

Problem scenario
You want to know the Docker images of the pods running in your Kubernetes cluster(s).  How do you determine this?

Solution
From your Kubernetes server, run this single multi-line command:

kubectl get pods --all-namespaces -o jsonpath="{..image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

It was taken from this link.

How Do You Deploy a LAMP Stack Application Powered by Kubernetes?

Problem scenario
You want to deploy your own LAMP stack with the power of Kubernetes.  You do not want to rely on official Docker Hub images for the underlying Docker containers.  How do you do this?

Solution
1.  Deploy Kubernetes to AWS.  If you need help to deploy Kubernetes to AWS, see this link.  If you need assistance installing kubectl on any type of Linux (CentOS/RHEL/Fedora, Debian/Ubuntu or SUSE), see this link

If you want to deploy it to Azure or Google Cloud Platform with these directions, know that the end product will not be fully functional.  Kubernetes can be fully functional in Azure or GCP (but not with these directions that rely on an Elastic Load Balancer [in AWS]).  These directions below rely on AWS for full functionality.  Using these directions to deploy Kubernetes to Azure or GCP is acceptable for the basic testing of using a Docker image that is not from Docker Hub.  If you still want to use these directions and use a custom Docker image with Kubernetes in Azure, see this link to deploy Kubernetes (in Azure).  See this link to deploy Kubernetes in Google Cloud Platform.  These directions below were designed for AWS to create a load balancer therein. To create worker nodes in AWS (to work with EKS), see this posting.

2.a. Run this command: kubectl get cluster-info

If you see 'error: the server doesn't have a resource type "cluster-info"', do not be alarmed, and proceed with these directions; go directly to step 2b. If you “The connection to the server localhost:8080 was refused – did you specify the right host or port?” when using AWS and EKS, see this posting.

2.b.  From your server with kubectl, in a directory where you can write to (e.g., cd /home/ubuntu), download these four files:

The above files are used for creating a Kubernetes cluster.  They are configured to take a Docker image from a Google repository when creating the Kubernetes cluster.  (The files are modified versions of files originally taken from https://github.com/heptio/example-lamp.) 

One way to obtain the files if your Linux server has access to the internet, is to create this script.  You can call it g.sh, and run it (with bash g.sh):

curl -Lk https://raw.githubusercontent.com/ContinualIntegration/kubernetes/master/googsecrets.yaml > /tmp/googsecrets.yaml
curl -Lk https://raw.githubusercontent.com/ContinualIntegration/kubernetes/master/googphp.yaml > /tmp/googphp.yaml
curl -Lk https://raw.githubusercontent.com/ContinualIntegration/kubernetes/master/googmysql.yaml > /tmp/googmysql.yaml
curl -Lk https://raw.githubusercontent.com/ContinualIntegration/kubernetes/master/googdata-loader-job.yaml > /tmp/googdata-loader-job.yaml

3.  Run these commands if you deployed Kubernetes anywhere except Google Cloud Platform:

kubectl create -f googsecrets.yaml
kubectl create -f googmysql.yaml
kubectl create -f googphp.yaml
kubectl create -f googdata-loader-job.yaml

kubectl get service googweb -o wide 
 # See if the load balancer gets created after 25 minutes.

If you deployed Kubernetes to GCP, run these commands:

kubectl create -f googsecrets.yaml --validate=false
kubectl create -f googmysql.yaml --validate=false
kubectl create -f googphp.yaml --validate=false
kubectl create -f googdata-loader-job.yaml
--validate=false

If you deployed this to a Kubernetes cluster in AWS, the external IP address will appear (e.g., after five minutes from the fourth command).  But if you deployed this to a Kubernetes cluster in Azure or GCP, do not expect to see an external IP address because it will not be created.  If you used Google Cloud Platform's Kubernetes Engine, you can click on "Workloads", "Discovery & load balancing", "Configuration", and "Storage" to see the results of the above commands.

How Do You Configure a Docker Host to Be a Client of a Docker Registry?

Problem scenario
Now that the Docker registry is set up and has been tested, you want the "docker login" command to be able to work from other Docker hosts.  How do you configure another server (a Docker host, as a client) to work with a Docker registry server?    

Solution
1.  Install Docker on the server that will be the client.  If you need directions, see this posting.  Once installed it will be referred to as the Docker host or the client.

2.  Copy the /etc/ssl/certs/ca-certificates.crt from the Docker registry server to the client server that is a Docker host.  For copying the .crt file with scp from the Docker registry server to new Docker hosts that will become the client servers of the registry, ensure that port 22 is initially open.  It can be closed later on.  

You may want to use this from the Docker registry server's back end: scp /etc/ssl/certs/ca-certificates.crt username@dockerclientFQDN:/tmp/ca-certificates.crt  
# where username is a username that can log into the Docker client server and dockerclientFQDN is the FQDN of the Docker client server.

You may not want to use sudo because that may cause scp to not work.  It is advisable to back up the /etc/ssl/certs/ca-certificates.crt on the Docker client first.  Then overwrite it with the file copied above.  It depends on how sensitive the client server is.  If it was just created or in a development environment, you likely would not need that .crt file backed up.

3.  Ensure that port 443 is open between the Docker registry server (inbound) and the client servers (outbound port 443).  You can use nmap to test.  If you are using AWS or Azure, you may need add a rule.  Your network engineer may need to open this port on a firewall that protects the servers.  This step is optional because a "docker login" command will fail if port 443 closed.  It is not that bad to find out late that port 443 is closed.

4.  If the FQDN of the Docker registry does not resolve from the Docker client (e.g., you cannot curl http://FQDNofDockerRegistry or ping FQDNofDockerRegistry because DNS is not configured) update the /etc/hosts file on the Docker client to map the IP address of the Docker registry server with the its FQDN.

5.  Make sure the Docker service is running on the client (e.g., sudo service docker start).  With Red Hat servers, it does not start automatically after it is installed.

6.  The basic way for a Docker client to connect to a registry is to use this command: "docker login http://FQDNofDockerRigstry" # with no quotes.  You will then need a username and password for the next prompts.

How Do You Deploy a Kubernetes Cluster to Azure?

Problem scenario
You want to deploy a Kubernetes cluster to Azure.  You have an Ubuntu Linux server (e.g., with a nano flavor in AWS, a server on-premises, or an one vCPU Azure server with one GB of RAM).  It has no software packages installed on it (e.g., it does not have Docker or Kubernetes).  How do you deploy Kubernetes into Azure (from an instance in AWS)?

Solution
Warning:  This will automatically create Azure servers behind the scenes.  It will cost money to do this.  The conjure-up program uses Juju and will create multiple machines.  Your Azure account will be billed if you follow these directions.

Prerequisites
You have an Azure account.  

Procedure
1.  Create an Ubuntu server.  If you are using AWS to deploy Kubernetes to Azure, the EC-2 instance can be the nano flavor or larger.  Any Ubuntu server should work as long as it has access to the internet.  These directions will work with a server in Azure, AWS or some other on-premises Ubuntu Linux server. If you are using Azure, see this posting.

2.  Log into the Ubuntu server.  Maximize the Putty session.

3.  Run these commands (to install Juju and conjure-up):

sudo apt-get -y update
sudo snap install conjure-up --classic
sudo apt-get -y install juju python libssl-dev libffi-dev python-dev build-essential
juju show-cloud azure
juju update-clouds
curl -L https://aka.ms/InstallAzureCli | sudo bash
# Respond to the prompts.  You may want the defaults.  
# An alternative directory for the install is /usr/lib/azure-cli.  
# For az executable, you may want /usr/bin
juju add-credential azure
# The name of the credential is arbitrary; just make one up you'll remember. 
# You'll enter your Azure username and password later. For "auth-type" choose "interactive".
# Obtain the subscription ID.  Log into the Azure Portal.  In the upper right click on your account and then click on "My permissions."  You should see an alphanumeric string near your subscription.
# Follow the prompts by opening a web browser from your workstation (separate from the Ubuntu Linux machine you were using) and go to the URL the Ubuntu machine tells you to.  Enter the code provided to authenticate.
# Sign in the with your Azure credentials.
conjure-up kubernetes

4.a.  At the menu of choices, choose "Kubernetes core."
4.b. For "Where would you like to deploy?" choose "Azure" and press enter.  
4.c.  To the next prompt, choose the Azure credentials you added earlier.

5.a.  Choose the region in Azure you would like your cluster to go to.  Press enter.
5.b. Choose "Deploy New Self-Hosted Controller."   Do NOT choose Juju-as-a-Service (JaaS) Managed controller.

6.  Enter the password for the ubuntu user at the prompt for a "sudo" password.

7.  When prompted, deploy all five remaining items.  When you see "Juju Controller is initializing. Please wait."  You may observe a delay on the step that says "Attempting to connect to x.x.x.x:22"  You may want to make sure your Ubuntu server is in a security group that accepts inbound connections to the IP address x.x.x.x.

8.  The unattended process may take 25 minutes.  You may want to monitor progress by watching two things.  First you may want keep an eye on your Putty session to your Ubuntu virtual server.  For example, it will look like this:

In your web browser you may want to log into the Azure Portal.  You may also want to refresh your Resources dashboard to watch the new machines being created.  Depending on which Azure region you chose, you may need to filter to see that region.

9.a.  If your Putty session to your Ubuntu server prompts you, choose "Run" to "Download the kubectl client program to your local host."  If you did this, choose "Run" again to the prompt about "kubectl get nodes" etc.  
9.b.  You may just see "Your deployment is now complete."  You'll also see a message that "Kubernetes master is now running at https://x.x.x.x:6443".  Go to this URL with a web browser.  You will be prompted for credentials.  To find these credentials, go to the Putty session and press "Enter" to quit.  Then run this command: kubectl config view

The results of this command will give you credentials for the "admin" user.

10.  Remember to delete those new Azure virtual machines or your Azure bill will be higher than you expected.

What Is the Difference between Integrity and Availability in the CIA Triad?

Updated on 12/26/18

Problem scenario

I.T. security concerns itself with confidentiality, integrity and availability.  These three categories are concepts which help professionals prioritize and crystallize what to secure and how to secure it.

In the context of computer security, the CIA triad (confidentiality, integrity and availability) is commonly used.  How could data be available if it did not have integrity?  What is the difference between integrity and availability?

Answer
Availability is the attribute of having data, infrastructure, or services accessible when they are needed or according to a service-level agreement.  Integrity governs the sheer accuracy of the data or the service.  The owner of a website that may support REST API services may intentionally spoof it by creating a superficial copy of the real website.  The copy may act as a substitute for development purposes.  A fake website such as this does not have integrity if it does not have all the REST API features that the real website has. 

Denial-of-service attacks obliterate availability -- but not necessarily the integrity of the data, infrastructure or service.  Unavailable data may have integrity.  Available data that is somewhat inaccurate is said to have low integrity.  In some contexts availability is more important than integrity.

Here is a theoretical example of having available data but without integrity.  Availability (of data) in a financial institution may be achievable with the help of store-and-forward servers.  (For example, Fiserv makes such a server; you used to be able to read about it here at https://www.fiserv.com/industries/bank-platforms/cleartouch/cleartouch-teller.aspx.)  These servers make funds available to customers when the main server of account balances is down for maintenance.  In theory these store-and-forward server may not connected to each other.  They may be connected to ATMs so customers can still process transactions without communication to the main server.  Thus a customer could illegally make withdrawals from different, independent cash-dispensing machines during the maintenance period.  These independent machines may dispense funds according to the last balance they were given before the maintenance period (via the store-and-forward servers).  Thus the cumulative withdrawal amount could be beyond what would have been dispensed had the ATMs been able to communicate with the centralized server.  In this example, data integrity and accuracy were lacking during the maintenance window, but transactional availability was preserved.

Store-and-forward TCP/IP servers may accumulate packets for future availability.  Integrity may be present during the storage period, but availability is lacking until the servers can forward the packets.

Another second (theoretical) example where you have data availability but not integrity is this there is a lack of accuracy in images or details of a story.  An institution may find the need to broadcast a graphic or a warning (due to weather or some terrorist development).  They may forgo integrity (some accuracy) for a legitimate need to immediately publish a message.  A grainy (fixable) image and/or facts without confirmed (or cross-referenced) details of a story can benefit those reading the message.  The timely  publication is associated with situations wherein availability of the message is more important than the integrity.

Details may be lost in the cross-network transmission of data.  Thus integrity may be somewhat absent, but availability is chiefly present.

A third and historic example was how CNN.com responded to the events of 9/11.  On September 11, 2001 the CNN website's content changed to ensure availability (1) (2).  They eliminated pictures to accommodate the spike in traffic to their website during the critical hours of that historic day (1) (2).  The integrity of the publication was deliberately compromised as the presentation would have included pictures.  But the availability was maintained.

You may want to read more about the three core attributes of security here:
http://panmore.com/the-cia-triad-confidentiality-integrity-availability

(1)
https://www.coursehero.com/file/p52s233/As-a-result-of-the-unprecedented-demand-news-websites-took-a-number-of-steps-to/

(2)
https://ww.washingtonpost.com/news/the-switch/wp/2013/09/11/how-the-internet-broke-on-911/?noredirect=on&utm_term=.5cf8c1cde669

How Do You Troubleshoot the Chef Error “Field ‘name” invalid” after Running a chef-server-ctl Command?

Problem scenario
You are trying to create an organization using the chef-server-ctl org-create command.  But you get an error that the "name" entered was invalid.  For example, you run this:

sudo chef-server-ctl org-create ContInt "ContinualIntegration" --association_user contintuser --filename /bin/good.pem

But you get this:

ERROR: The data in your request was invalid
Response: Field 'name' invalid

What should you do?

Solution
For the "shortname" that is immediately after the "org-create" command, make sure it is all lowercase.  Instead of "ContInt", it should be "contint".

How Will Trends in DevOps and Automation Affect SDETs?

Question
Will current trends in DevOps and automation affect the role SDETs play?

Answer
If you want a definition of an SDET (and its etymology), see this posting. (As of 9/2/19, Wikipedia has no definition of the job title SDET.) If you Google the phrase "devops venn diagram" for images you will not see pictures of merely two overlapping circles with one circle representing "development" and another representing "operations."  You will see DevOps in the middle of an intersection of three circles -- one of the circles is also QA!  Moreover we may see a fourth circle for security in future Venn diagrams for DevOps; that is we may see DevOps being at the intersection of four circles symbolizing development, operations, quality assurance and security.  Some people think that DevOps with security will remain its own specialized, combinational focus; to learn more about DevOps and security, see this article

The DevOps movement is about combinations of specialties or getting away from specialization in favor of generalization; it is also about automation. For many of us, DevOps is about automating what you can program and programming what you can automate.  There are benefits and limitations to automation.  Humans can do non-systematic things that computers cannot do.  In Nietzsche's Twilight of the Idols, the philosopher says "I distrust all systematizers, and avoid them. The will to a system, shows a lack of honesty."  SDETs of tomorrow will still do what Quality Assurance Engineers did yesterday: manually find bugs that must be fixed.  Bill Gates said many years ago that there are two rules of automation:

'1.    “The first rule of any technology used in a business is that automation applied to an efficient operation will magnify the efficiency. 
2.    The second is that automation applied to an inefficient operation will magnify the inefficiency."’ (Capgemini 2015)

Release automation is very powerful.  Reliance on automation may grow, but human professionals will remain crucial.  While the book How We Test Software at Microsoft was published before DevOps became a common word in the I.T. world, it is interesting that it said this: "Testers who understand programming concepts and computer architecture typically have the analysis skills that testing requires" (page 23).  The requirements for a normal software tester are not necessarily high.  In fact, being enthusiastic about testing may be sufficient.  How We Test Software at Microsoft says "[t]he concept of hiring a software engineer with a passion for testing is powerful and is the biggest differentiator between the Microsoft approach to software testing and the typical industry approach" (page 23).  It is important to note that prestigious hedge funds have followed Microsoft's lead in leveraging SDETs in their critical operations.  In 2015 it was observed that the SDET role was becoming more popular due to Microsoft itself according to Ness.com. (The Ness.com article link used to be https://www.ness.com/significance-of-a-software-development-engineer-in-test-sdet-in-the-digital-economy/).

Some people think that "genius is as common as dirt" (taken from Weapons of Mass Instruction).  Open source contributions come not just from institutions, but from eager job seekers doing spikes or from everyday computer enthusiasts.  Sheer interest in interdisciplinary subjects and contextual knowledge about an environment may be more important than formal training.  Can structured classroom work keep pace with the rate that technologies evolve?  How We Test Software at Microsoft says that Microsoft did not hire "subject matter experts (SMEs) as testers" (page 23).  Thus the DevOps trend toward generalization may trace some of its roots to Microsoft's aversion toward SMEs.  Passionate individuals may not be content to specialize in one area thus to satiate their desire study multiple fields.  Corporate executives in the I.T. industry should take notice that these passionate people may be the biggest contributors to the experimental and bleeding edge side of the DevOps movement.

According to a 2015 study co-sponsored by HP and others, DevOps will become a driver of testing.  This finding, taken in combination with the 2008 How We Test Software at Microsoft, proves that DevOps engineers with a passion for discovering problems, would make excellent SDETs.  Given DevOps engineers knowledge of architecture and programming, their penchant for integrating disciplines while using automation, a test-oriented mentality could make them phenomenally productive.  Knowledge of network and operating system environments can be helpful for SDETs writing automation to ensure that a program is robust with the underlying platforms.

InformationWeek indicates, in their article published on 11/9/17, that businesses should spend more on software testing and possibly less on DevOps.  The article found that DevOps has disadvantages if not implemented correctly.  The article clearly indicates that a human running tests can address the limitations of automated testing.  Spotify perceives testing to be a creative act and chooses to employ people to do manual testing (TechBeacon). 

DevOps.com published an article in 2016 saying that "DevOps Does Not Need QA."  They may be somewhat biased toward automation and overly optimistic about DevOps jobs.  This website is primarily dedicated to technical DevOps topics (such as configuration management tools, systems administration, programming, and infrastructure automation).  The Economic Times' CIO website recently published an article saying that DevOps groups are preferable to QA groups to transform a business toward a more agile state.

QA groups in 2017 are not necessarily composed of many SDETs.  Cigniti.com's blog says that "by early 2020, almost all testers will need to wear an SDET hat to be successful in the field of Test Automation, that is going to become mainstream."  A TechBeacon article, which seems to have been published in 2016, says that developers are now writing their own tests.  This same article indicates that "test professionals" are better at writing performance test automation than regular developers.  This admiration of specialization in testing contrasts with Microsoft's 2008 view that passion is key for obviating the need for subject matter experts (pages 23 and 24 of How We Test Software at Microsoft).  It is unclear if Microsoft has revised their opinion, but it could be that Microsoft knows better than TechBeacon as the term SDET came from them. 

In 2016 we started to see the emergence of the term "QA in Production" (SlideShare.net and MartinFowler.com).  Spotify finds that it is cost-effective to do testing in its production environment (TechBeacon).  Releases must happen rapidly in today's business world.  Monitoring has become more sophisticated, and quality is important given the amount of competition software companies have. 

Software consultant Viktor Farcic's view is that a siloed or "specialized" DevOps engineering department defeats the purposes of DevOps culture (technologyconversations.com).  Despite this, dedicated DevOps teams do exist.  In fact "[s]killed SDETs also often operate as part of a DevOps team in various capacities" (Aim Consulting).  Farcic may be a visionary when he says that "[t]he level of collaboration between different professionals is directly disproportional to the number of barriers between them" (technologyconversations.com).  The integration of professionals from multiple disciplines and backgrounds such as developers, fault-finding manual testers, skilled SDETs with refined knowledge in building automation tools, systems administrators who are knowledgeable of operating systems and where to find logs for root cause analysis can lead to the most comprehensive testing possible. 

Facebook relies on automation for testing without manual testers (TechBeacon).  "Both [Facebook and Google] companies emphasize personal responsibility (with Facebook shaming engineers that commit bad code), and see testing as part of the culture, not just a department somewhere in the building that has to clean up after the developers" (Business 2 Community).  We know as collaboration across functional experts grows, SDETs often contribute to product development with code and design features for the software that they test.  Cigniti.com's blog says that corporate Test Centers of Excellence (sophisticated QA departments) will be phased out because "[t]est automation developers today are now a part of the agile teams."  The blog also says "that software testing budgets will continue to grow."  The SDET role has been adopted by many companies that are exemplary in the DevOps movement.  DevOps is about bringing development closer to a second function (operations).  Thus the merging of development and QA produces the relatively new SDET role that is not siloed in a QA department but integrated with developers. 

Job titles and professional focus may not lend themselves to exhibits at The Computer History Museum. There is little doubt that the modern day DevOps movement with the emergence of SDETs and relatively ambitious automation goals will make interesting history for what follows.  We may look back at 2017 as an "early DevOps era" before many DevOps professionals transitioned into SDETs and the rest matured professionally into "DevOps 2.0 engineers."  They may all work in one large group without departmental confinement achieving productivity we could barely imagine.  The future may be that most I.T. professions will merge into one job title that did not exist in 2017.

The era hereafter may be without a scarcity of goods due to artificial intelligence.  There are already predictions that learning algorithms can do the testing reliably and comprehensively (TechBeacon).  Will automation with machine learning make us free from manual testing of software as well as the manual development of software?

What Is an SDET?

Question
What is an SDET and where did the term originate?

Answer
The book How We Test Software at Microsoft says that the formal title for a software tester at Microsoft is "Software Development Engineer in Test, or SDET" (page 23).  This SDET acronym (free of punctuation) was adopted in 2005 (page 24 of the above referenced book). Microsoft's adoption of SDET gave this acronym recognition and serious meaning given the company's leadership in the industry.  There seems to be no evidence that the term SDET was widespread before Microsoft employed the job title.  The "T" stands for "Test" while the preposition "in" is not referred to in the acronym.  The term is pronounced "ess-det," like the letter "s" as the first syllable and the word "debt" as the second syllable.  The word "debt" rhymes with the word "pet" (as you can hear with this Microsoft Ireland video on Youtube).

How We Test Software at Microsoft says that a regular software engineer at Microsoft is called "Software Development Engineer, or SDE" (page 23).  This book, written by Microsoft employees, says that "[t]he similarity in names of the two disciplines is by design because testers at Microsoft are developers" (page 23).  So an SDET position was designed for an employee to develop code to test software.  Whereas regular software engineers design code, SDETs specialize in finding bugs and using automation to test the software.  Microsoft is still completely aware that not all testing can be automated.  This book, How We Test Software at Microsoft, was published in December of 2008, but it still has relevance 10 years later. 

Professionals who test software in a traditional method with manual procedures can be given the job title of one (but not limited to) of the following: Quality Assurance Analyst, Quality Assurance Engineer, Quality Control Analyst, or Quality Assurance Engineer.  Quality Control focuses on production defects whereas Quality Assurance attempts to set guidelines for the development process (according to TechTarget).  Traditional software testers (e.g., a QA Engineer) may design unit tests for atomic features of software.  Both QA analysts and SDETs test software, but SDETs create automation to do it.  SDETs may create a script or a comprehensive framework for much of the testing itself.  SDETs may examine performance, automate tasks, or write complex programs to check reliability beyond manual testing of specific functions of software.  SDETs often participate in the development of code whereas QA analysts might not (Software Testing Class).  SDETs are more frequently part of the development team itself rather than being an employee of a separate QA department.  SDETs are a hybrid between a QA analyst (a manual software tester) and a software engineer. The bottom of this article has links to other websites to define an SDET with greater details. 

In Eric S. Raymond's book The Cathedral and the Bazaar (on page 33), he says that developers focus on the theoretical aspects of the code whereas testers focus on the pragmatic behavior of the software. We see that SDETs to some extent blend the best of both worlds. As of May 2020 we are not sure if SDETs with developer backgrounds are preferred in the industry's recruitment process over those with QA backgrounds.

DevOps engineers are thought of as hybrids of developers and operations analysts (or systems administrators).  SDETs merge the disciplines of QA and development in very much the same way DevOps engineers merge the disciplines of development and operations.  SDETs leverage automation in their daily work in the same way DevOps engineers leverage automation in their daily work.  Wheres as a DevOps engineer may automate an operations task (the creation of a virtual server), an SDET will automate a series of tests to find bugs in a new version of software (write a Python script to run Selenium tests).  For code that is deployed at a large scale (i.e., across thousands of servers), testing every unit in every environment is possible with automation; in the past testing may have been limited to a portion of an environment that was thought to be a representative sample.

SDETs may write test harnesses in code to do sophisticated quality assurance.** For example they may write code for observability via leveraging metaprogramming in the code that is being tested.***

For software that is unexpectedly valuable and has a useful lifespan longer than expected, the investment in testing automation can pay off greater than predicted.  The book How We Test Software at Microsoft says that Microsoft never planned on supporting Windows 95 through the year 2005 (page 27).  Some software has a useful life far longer than expected.  Therefore SDETs can write automation scripts or frameworks that can be of more value than what their company initially estimated.  Automation is key to putting software through larger numbers of tests too.

Gene Kim's IT Revolution website says that "[n]ow when any Google developer commits code, it is automatically run against a suite of hundreds of thousands of automated tests."  Google uses the title SET (which stands for Software Engineer in Test) instead of the term SDET.  Both Microsoft and Google believe in introducing new code or refactoring existing code purely for the sake of testability (page 67 of How We Test Software at Microsoft and Infoq.com).

If you search for "Software Engineer in Test" or "Software Development Engineer in Test" on Dice.com or Indeed.com, you will see that many companies have adopted the words of the SDET acronym as opposed to the Google equivalent or near-equivalent (the acronym SET).  Fidelity Investments uses the term "Software Engineer in Test."  It appears that IBM and HP do not use either of these job titles (as of November of 2017), but Apple seems to use both “Software Development Engineer in Test" as well as “Software Engineer in Test" (based on job searches for these two titles on each of these three companies’ websites).  Amazon’s job site shows that they use SDET but use the word "Dev" as in “Software Dev Engineer in Test" for their version of the job title.  SDETs can work in a variety of departments depending on the company.  Evidently they initially worked in the Test department of Microsoft.

According to TechBeacon Microsoft employs approximately two SDETs for every three regular software engineers.  According to the book How We Test Software at Microsoft, Microsoft employed one tester (either an SDET or a traditional, manual-testing professional) for every one regular software engineer (page 31).  At Google there are far fewer SETs for every regular software engineer.  This may have less to do with the prevailing culture and more to do with the products offered.  Microsoft's products are mostly shippable and have to be compatible with different hardware.  While Android needs to run on different hardware too, other than Chrome many of Google's products are SaaS offerings.  Business 2 Community reports that "Google’s achieved this [having relatively small number of testers] by creating a culture of testing, and not an abstract, disconnected testing department."  The lack of disconnection and mostly integrated aspect of testing is core to the DevOps movement itself.

The book How We Test Software at Microsoft indicates that security is an important part of testing along with "ilities" such as just reliability, maintainability, usability and portability (page 250).  Having secure products helps build trust with consumers and employees of a high-tech company.

In most of years of their existence, Google grew with the motto "[d]on't be evil."  They ostensibly still have a code (no pun intended) of conduct to act honorably.  This niceness has helped their testing efforts too.  Not many companies have the quality and quantity of software testing that Google has, especially with their staffing ratios of comparatively few Software Engineers in Test.  IT Revolution reports "[w]hat enables this system to work at Google is engineering professionalism and a high-trust culture that assumes everyone wants to do a good job, as well as the ability to detect and correct issues quickly."

Google's institutional commitment toward quality software involves a blend of development and testing.  They practice shift-left testing and have a corporate-wide testing culture (not reduced to a mere department or a practice confined to group) with ethical conduct for establishing trust among employees.  Their SETs have helped create a software program called the Google Test Case Manager too.  Perhaps the Google Test Case Manager will be made available to the public and make the same impact as other Google contributions have made.*  To learn more read the book How Google Tests Software.  Google is not alone in believing that trust is important for employees working more closely together (DevOps.com). Google was not the first to recommend testing should be incorporated in the initial development process; the book The Mythical Man Month (on page 69) says that testing should be done "early and simultaneously" in the design of software.

White papers published from Google were the basis for Hadoop.  Google open sourced Kubernetes (ACM).
** The book The Mythical Man-Month says "[m]ethods of designing programs so as to eliminate or at least illuminate side effects can have an immense payoff in maintenance costs."
*** "Metaprogramming is a technique of writing computer programs that can treat themselves as data, so they can introspect, generate, and/or modify itself while running." (This was taken from page 158 of Expert Python Programming. The newer 4th edition is available here.)

Additional Information and Citations
The book How We Test Software at Microsoft uses the term DNA to describe the attributes of an effective SDET (page 23).  Google also uses the term DNA to describe the traits of an ideal SET and not SDET (GoogleBlog).  DevOps.com has an article about how an SDET spends time on an average day.

To learn more about the contradistinction between an SDET, a software tester, and a software engineer, you may want to read these links.
(1) https://www.infoq.com/news/2011/03/Ensuring-Product-Quality-Google
(2) http://www.softwaretestingclass.com/what-is-difference-between-sdet-and-tester/
(3) https://softwareengineering.stackexchange.com/questions/191913/what-is-a-developer-in-test
(4) https://sqa.stackexchange.com/questions/25119/what-does-a-software-development-or-software-engineer-in-test-do
(5) https://sqa.stackexchange.com/questions/6408/what-is-the-difference-between-software-test-engineer-test-automation-engineer

How Do You Write a Tic-Tac-Toe Game in Python?

Updated on 9/17/19 to support version 2 and version 3 of Python

Arguably the oldest video games was a variation of Tic-tac-toe.  To read more about this, see this link.  Many English-speaking countries outside the U.S. refer to the game as Noughts and Crosses (according to Wikipedia). 

In the U.S., November 11th is the day America observes Veteran's Day.  This day in 2018 will mark the 100th anniversary of the public holiday. (1)  To learn more about the history of the celebration, see this link

----------------------------------------------------------------------------------------------------------------------------------

If you have Python installed, two people can share a keyboard and use the Python program below.  Save the program as tictactoe.py and run it with "python tictactoe.py".

# Updated on 8/29/21
# Tic-tac-toe game in Python 3. Written by continualintegration.com.
print("This is a two player game of tictactoe.  One person can pretend to be the other player.")
print("Both players should share a keyboard and monitor.")
print("The legend for squares in the grid is as follows: ")
print("                                                                ")
print("***************************************************************")
print("ltc is left-top-corner, tm is top-middle, rtc is right-top-corner")
print("lm is left-column-middle-row, c is center, mr is middlerow-right-column")
print("lbc is left-bottom-corner, bm is bottom-middle, rbc is right-bottom-corner")
print("***************************************************************")
print("                                                                ")
print("Enter a cell based on the legend above.")

r = 1
mark = 'z'
goodlist = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

first = input("Player 1, please enter which location you want to mark with X: ")
w, hh = 8, 5
a, b, c, d, e, f, g, h, i, j = ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
board = [[0 for x in range(w)] for y in range(hh)]

board[1][0] = '|'
board[1][1] = a
board[1][2] = '|'
board[1][3] = b
board[1][4] = '|'
board[1][5] = c
board[1][6] = '|'

board[2][0] = '|'
board[2][1] = d
board[2][2] = '|'
board[2][3] = e
board[2][4] = '|'
board[2][5] = f
board[2][6] = '|'

board[3][0] = '|'
board[3][1] = g
board[3][2] = '|'
board[3][3] = h
board[3][4] = '|'
board[3][5] = i
board[3][6] = '|'

toprow = ''
midrow = ''
bottomrow = ''


def printer(goodlist):
    board[1][0] = '|'
    board[1][1] = goodlist[0]
    board[1][2] = '|'
    board[1][3] = goodlist[1]
    board[1][4] = '|'
    board[1][5] = goodlist[2]
    board[1][6] = '|'

    board[2][0] = '|'
    board[2][1] = goodlist[3]
    board[2][2] = '|'
    board[2][3] = goodlist[4]
    board[2][4] = '|'
    board[2][5] = goodlist[5]
    board[2][6] = '|'

    board[3][0] = '|'
    board[3][1] = goodlist[6]
    board[3][2] = '|'
    board[3][3] = goodlist[7]
    board[3][4] = '|'
    board[3][5] = goodlist[8]
    board[3][6] = '|'
    toprow = ''
    midrow = ''
    bottomrow = ''
    for x in range(0, 7):
        toprow = toprow + board[1][x]
        midrow = midrow + board[2][x]
        bottomrow = bottomrow + board[3][x]
    print("-------")
    print(toprow)
    print(midrow)
    print(bottomrow)
    print("-------")
    return


def marker(loc, mark, r):
    if (loc == 'ltc'):
        a = mark
        callagain(loc, r)
    elif (loc == 'tm'):
        b = mark
        callagain(loc, r)  # no c on purpose
    elif (loc == 'rtc'):
        d = mark
        callagain(loc, r)
    elif (loc == 'lm'):
        e = mark
        callagain(loc, r)
    elif (loc == 'c'):
        f = mark
        callagain(loc, r)
    elif (loc == 'mr'):
        g = mark
        callagain(loc, r)
    elif (loc == 'lbc'):
        h = mark
        callagain(loc, r)
    elif (loc == 'bm'):
        i = mark
        callagain(loc, r)
    elif (loc == 'rbc'):
        j = mark
        callagain(loc, r)
    else:
        print("************Invalid input******************")
        r = r - 1


def checker(goodlist):
    for x3 in range(1, 4):  # the variable's name is "x3".  This section is for horizontal wins.
        if board[x3][1] == board[x3][3]:
            if board[x3][3] == board[x3][5]:
                if board[x3][5] == 'X':
                    print("Player 1 Wins!!!!!")
                    var1 = "stop"
                    quit()
                elif board[x3][5] == 'O':
                    print("Player 2 Wins!!!!!")
                    var1 = "stop"
                    quit()
        else:
            var1 = "go"
    for y3 in range(1, 6):   # the variable's name is "y3"   This section is for vertical wins.
        if board[1][y3] == board[2][y3]:
            if board[2][y3] == board[3][y3]:
                if board[3][y3] == 'X':
                    print("Player 1 Wins!!!!!")
                    var1 = "stop"
                    quit()
                elif board[3][y3] == 'O':
                    print("Player 2 Wins!!!!!")
                    var1 = "stop"
                    quit()
        else:
            var1 = "go"
    # Diagonal checking below.
    if board[1][1] == board[2][3]:
        if board[2][3] == board[3][5]:
            if board[3][5] == 'X':
                print("Player 1 Wins!!!!!")
                var1 = "stop"
                quit()
            elif board[3][5] == 'O':
                print("Player 2 Wins!!!!!")
                var1 = "stop"
                quit()
            else:
                var1 = "go"
    if board[1][5] == board[2][3]:
        if board[2][3] == board[3][1]:
            if board[3][1] == 'X':
                print("Player 1 Wins!!!!!")
                var1 = "stop"
                quit()
            elif board[3][1] == 'O':
                print("Player 2 Wins!!!!!")
                var1 = "stop"
                quit()
            else:
                var1 = "go"
    catgamechecker(goodlist)  # See if it is a Cat's game (aka a tie).
    return var1


def catgamechecker(goodlist):
    if (goodlist[0] != " "):
        if (goodlist[1] != " "):
            if (goodlist[2] != " "):
                if (goodlist[3] != " "):
                    if (goodlist[4] != " "):
                        if (goodlist[5] != " "):
                            if (goodlist[6] != " "):
                                if (goodlist[7] != " "):
                                    if (goodlist[8] != " "):
                                        print("Cat's Game.  Game over.  It was a tie.")
                                        var1 = "stop"
                                        quit()


def playeralt(let, r):
    if checker(goodlist) == "go":
        if (r % 2 == 0):
            mark = 'X'
            call2(let, mark, r)
            if aw == "go":
                print("Player 2, where should an O go?")
        else:
            mark = 'O'
            call2(let, mark, r)
            if aw == "go":
                print("Player 1, where should an X go?")
    else:
        quit()


def callagain(let, r):
    a = checker(goodlist)
    playeralt(let, r)


def call2(let, mark, r):
    if (let == 'ltc'):
        subcall2(let, mark, r, 0)
    elif (let == 'tm'):
        subcall2(let, mark, r, 1)  # goodlist[1]=mark
    elif (let == 'rtc'):
        subcall2(let, mark, r, 2)  # goodlist[2]=mark
    elif (let == 'lm'):
        subcall2(let, mark, r, 3)  # goodlist[3]=mark
    elif (let == 'c'):
        subcall2(let, mark, r, 4)  # goodlist[4]=mark
    elif (let == 'mr'):
        subcall2(let, mark, r, 5)  # goodlist[5]=mark
    elif (let == 'lbc'):
        subcall2(let, mark, r, 6)  # goodlist[6]=mark
    elif (let == 'bm'):
        subcall2(let, mark, r, 7)  # goodlist[7]=mark
    elif (let == 'rbc'):
        subcall2(let, mark, r, 8)  # goodlist[8]=mark
    else:
        print("*****************Invalid input************")
        r = r - 1
    printer(goodlist)
    checker(goodlist)
    r += 1
    print("********************************")
    if r % 2 == 0:
        player = "Player 1"
        md = 'X'
    else:
        player = "Player 2"
        md = 'O'
    loc = input(player + " please enter which location you want to mark with an " + md + ": ")
    checker(goodlist)
    callagain(loc, r)  # marker(loc, mark, r)


def subcall2(let, mark, r, glnum):
    if (goodlist[glnum] == ' '):
        goodlist[glnum] = mark
    else:
        print("That square was already taken. Please try again!")
        print("Player with " + mark + " needs to go again.")
        if r % 2 == 0:
            player = "Player 1"
            md = 'X'
        else:
            player = "Player 2"
            md = 'O'
        loc = input(player + " please enter which location you want to mark with an " + md + ": ")
        checker(goodlist)
        callagain(loc, r)


marker(first, 'X', 0)

# The program above is for Python 3 is above. The program below is for Python 2.

# Tic-tac-toe game in Python 2. Written by continualintegration.com.
# Updated on 8/29/21
print "This is a two player game of tictactoe.  One person can pretend to be the other player."
print "Both players should share a keyboard and monitor."
print "The legend for squares in the grid is as follows: "
print "                                                                "
print "***************************************************************"
print "ltc is left-top-corner, tm is top-middle, rtc is right-top-corner"
print "lm is left-column-middle-row, c is center, mr is middlerow-right-column"
print "lbc is left-bottom-corner, bm is bottom-middle, rbc is right-bottom-corner"
print "***************************************************************"
print "                                                                "
print "Enter a cell based on the legend above."

r = 1
mark = 'z'
goodlist = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

first = raw_input("Player 1, please enter which location you want to mark with X: ")
w, hh = 8, 5;
a, b, c, d, e, f, g, h, i, j = ' ', ' ',' ', ' ',' ', ' ',' ', ' ',' ', ' '
board = [[0 for x in range(w)] for y in range(hh)]

board[1][0]='|'
board[1][1]= a
board[1][2]= '|'
board[1][3]= b
board[1][4]= '|'
board[1][5]= c
board[1][6]= '|'

board[2][0]='|'
board[2][1]= d
board[2][2]= '|'
board[2][3]= e
board[2][4]= '|'
board[2][5]= f
board[2][6]= '|'

board[3][0]='|'
board[3][1]= g
board[3][2]= '|'
board[3][3]= h
board[3][4]= '|'
board[3][5]= i
board[3][6]= '|'


toprow = ''
midrow = ''
bottomrow = ''

def printer(goodlist) :
  board[1][0]='|'
  board[1][1]= goodlist[0]
  board[1][2]= '|'
  board[1][3]= goodlist[1]
  board[1][4]= '|'
  board[1][5]= goodlist[2]
  board[1][6]= '|'

  board[2][0]='|'
  board[2][1]= goodlist[3]
  board[2][2]= '|'
  board[2][3]= goodlist[4]
  board[2][4]= '|'
  board[2][5]= goodlist[5]
  board[2][6]= '|'

  board[3][0]='|'
  board[3][1]= goodlist[6]
  board[3][2]= '|'
  board[3][3]= goodlist[7]
  board[3][4]= '|'
  board[3][5]= goodlist[8]
  board[3][6]= '|'
  toprow = ''
  midrow = ''
  bottomrow = ''
  for x in range(0, 7):
     toprow = toprow + board[1][x]
     midrow = midrow + board[2][x]
     bottomrow = bottomrow + board[3][x]
  print "-------"
  print toprow
  print midrow
  print bottomrow
  print "-------"
  return


def marker(loc, mark, r):
  if (loc == 'ltc'): a=mark; callagain(loc, r)
  elif (loc == 'tm'): b=mark; callagain(loc, r) # no c on purpose
  elif (loc == 'rtc'): d=mark; callagain(loc, r)
  elif (loc == 'lm'): e=mark; callagain(loc, r)
  elif (loc == 'c'): f=mark; callagain(loc, r)
  elif (loc == 'mr'): g=mark; callagain(loc, r)
  elif (loc == 'lbc'): h=mark; callagain(loc, r)
  elif (loc == 'bm'): i=mark; callagain(loc, r)
  elif (loc == 'rbc'): j=mark; callagain(loc, r);
  else: print "************Invalid input******************"; r = r -1;

def checker(goodlist):
  for x3 in range(1,4): #the variable's name is "x3".  This section is for horizontal wins.
    if board[x3][1] == board[x3][3]:
      if board[x3][3] == board[x3][5]:
        if board[x3][5] == 'X': print "Player 1 Wins!!!!!"; var1 = "stop"; quit()
        elif board[x3][5] == 'O': print "Player 2 Wins!!!!!"; var1 = "stop"; quit()
        else: var1 = "go"
  for y3 in range(1,6):   #the variable's name is "y3"   This section is for vertical wins.
    if board[1][y3] == board[2][y3]:
      if board[2][y3] == board[3][y3]:
        if board[3][y3] == 'X': print "Player 1 Wins!!!!!"; var1 = "stop";  quit()
        elif board[3][y3] == 'O': print "Player 2 Wins!!!!!"; var1 = "stop"; quit()
        else: var1 = "go"
  # Diagonal checking below.
  if board[1][1] == board[2][3]:
    if board[2][3] == board[3][5]:
      if board[3][5] == 'X': print "Player 1 Wins!!!!!"; var1 = "stop"; quit()
      elif board[3][5] == 'O': print "Player 2 Wins!!!!!"; var1 = "stop"; quit()
      else: var1 = "go"
  if board[1][5] == board[2][3]:
    if board[2][3] == board[3][1]:
      if board[3][1] == 'X': print "Player 1 Wins!!!!!"; var1 = "stop"; quit()
      elif board[3][1] == 'O': print "Player 2 Wins!!!!!"; var1 = "stop"; quit()
      else: var1 = "go"
  catgamechecker(goodlist)  # See if it is a Cat's game (aka a tie).
  return var1

def catgamechecker(goodlist):
  if (goodlist[0] != " "):
    if (goodlist[1] != " "):
      if (goodlist[2] != " "):
        if (goodlist[3] != " "):
          if (goodlist[4] != " "):
            if (goodlist[5] != " "):
              if (goodlist[6] != " "):
                if (goodlist[7] != " "):
                  if (goodlist[8] != " "):
                        print "Cat's Game.  Game over.  It was a tie."
                        var1 = "stop";
                        quit()

def playeralt(let, r):
  if checker(goodlist) == "go":
    if r % 2 == 0:
      mark = 'X'
      call2(let, mark, r)
      if aw == "go": print "Player 2, where should an O go?"
    else:
      mark = 'O'
      call2(let, mark, r)
      if aw == "go": print "Player 1, where should an X go?"
  else:
      quit()

def callagain(let, r):
  a = checker(goodlist)
  playeralt(let, r)

def call2(let, mark, r):
  if (let == 'ltc'): subcall2(let, mark, r, 0);
  elif (let == 'tm'): subcall2(let, mark, r, 1); #goodlist[1]=mark;
  elif (let == 'rtc'): subcall2(let, mark, r, 2); #goodlist[2]=mark;
  elif (let == 'lm'): subcall2(let, mark, r, 3);  #goodlist[3]=mark;
  elif (let == 'c'): subcall2(let, mark, r, 4); #goodlist[4]=mark;
  elif (let == 'mr'): subcall2(let, mark, r, 5); #goodlist[5]=mark;
  elif (let == 'lbc'): subcall2(let, mark, r, 6); #goodlist[6]=mark;
  elif (let == 'bm'): subcall2(let, mark, r, 7); #goodlist[7]=mark;
  elif (let == 'rbc'): subcall2(let, mark, r, 8); #goodlist[8]=mark;
  else: print "*****************Invalid input************"; r = r -1;
  printer(goodlist)
  checker(goodlist)
  r += 1
  print "********************************"
  if r % 2 == 0:
    player = "Player 1"; md = 'X'
  else: player = "Player 2"; md = 'O';
  loc = raw_input(player + " please enter which location you want to mark with an " + md + ": ")
  checker(goodlist)
  callagain(loc, r) #marker(loc, mark, r)

def subcall2(let, mark, r, glnum):
  if (goodlist[glnum] == ' '): goodlist[glnum]=mark;
  else:
      print "That square was already taken. Please try again!"
      print "Player with " + mark + " needs to go again."
      if r % 2 == 0:
        player = "Player 1"; md = 'X'
      else: player = "Player 2"; md = 'O';
      loc = raw_input(player + " please enter which location you want to mark with an " + md + ": ")
      checker(goodlist)
      callagain(loc, r)

marker(first, 'X', 0)

How Do You Write a Chef Recipe to Deploy a Apache Web Server to Linux Servers?

Problem scenario
You have Linux servers that need Apache web server installed on them.  How do you write a Chef recipe to deploy Apache web server?

Solution
Prerequisite

This solution requires that the Chef server must have access to GitHub.  If you want to install Chef server, see this posting.  If you want to install Chef client, see this posting.  You will need Git installed on the Chef server too.

Procedures
1. Go to the Chef server.  

2.  Go to the cookbooks directory (e.g., sudo find / -name knife.rb).  Find the "cookbook_path" stanza in this file.  If you need to create this file (e.g., you recently configured knife but did not create the path), do so now.  Then cd into it.

3.  Run this command:
git clone https://github.com/ContinualIntegration/httpd.git httpd

4.  From the Chef server if knife has been installed and you are not using the ChefDK, or from a workstation with knife and you are not using the ChefDK on the Chef server, run this command:

knife cookbook create httpd

Otherwise run this command:

chef generate cookbook httpd

5.  Run this command:

knife cookbook upload httpd

5. Do these steps:
    a) knife node edit <nameOfNode>  # Where "<nameOfNode>" is the FQDN of the Chef client server.
    b)  Under the run list, add no commas if it is currently empty, otherwise put a comma after the last (or single) recipe you find in the run list, then add this stanza:
        "recipe[httpd]"

    c)  Save the changes.

6.  You are done creating the recipe.  You can now run the Chef client.