tag:blogger.com,1999:blog-91089448284773465912024-03-18T21:41:15.539+00:00Hamid Shahid's WeblogHamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.comBlogger96125tag:blogger.com,1999:blog-9108944828477346591.post-53333968748250331762018-08-08T09:07:00.003+01:002018-08-09T17:54:45.094+01:00The Curious Case of Shared Libraries in Microservice Architecture<div dir="ltr" style="font-family:Arial;font-size:15pt;text-align: left;" trbidi="on">
<div>
Unless you have been living under a rock in the last few years, you would have heard of <a href="https://en.wikipedia.org/wiki/Microservices" target="_blank">Microservices</a>. If you don't quite know</div>
<blockquote class="tr_bq">
<i>Microservices are autonomous services with a bounded context, are fully self-contained and have their own release cadence. </i></blockquote>
<div>
In other words, Microservices can be created, deployed and released independently. Their independence enables quick turnaround. Teams can release one version after another of an ever improving service without depending upon or worrying about anybody else. A microservice essentially own its own data store, CI/CD process, deployment and everything that goes with it. A team can choose its own technology stack and build something independent of other builds.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
What about "code reuse"</h3>
<div>
<a href="https://en.wikipedia.org/wiki/Reusability" target="_blank">Reusability</a> has always been a fundamental principle in software engineering and one way of reusability is <a href="https://en.wikipedia.org/wiki/Code_reuse" target="_blank">code reuse</a>. Most often this is achieved using <a href="https://en.wikipedia.org/wiki/Code_reuse" target="_blank">code libraries</a>. In the last 20 years, I have seen an evolution of technology for creation and consumption of code libraries from C++ libraries to COM and ActiveX objects to JAR files and .NET libraries to NuGet packages and Node modules. Regardless of technology, common libraries has always been around and using them has been a desirable thing.</div>
<div>
<br /></div>
<div>
However, the general default advise in a Microservice architecture is</div>
<blockquote class="tr_bq">
<i>"Don’t share common libraries between Microservices." </i></blockquote>
<div>
Why? Because, common libraries creates coupling.</div>
<div>
<br /></div>
<div>
As an example, consider a team let's say "Team A" created a brilliant library to make web requests when they created "Microservice A". Another team "Team B" started creating "Microservice B" and decided to use library created by "Team A" to make web requests. All good so far. In the 2nd iteration, they realized that they needed to enhance the web request library. Now, they would either need Team A to update it for them or do it for them. If they change the library, it would have an impact on "Microservice A" as well. This is undesirable.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Is Shared Libraries between Microservices a definite No-no?</h3>
<div>
Well… there are differing opinions on this. There is an apparent conflict between the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" target="_blank">Don't Repeat Yourself (DRY)</a> and <a href="https://en.wikipedia.org/wiki/Loose_coupling" target="_blank">Loose Coupling principle</a>. Some pundits argue overwhelmingly <a href="https://blog.philipphauer.de/dont-share-libraries-among-microservices/" target="_blank">not to use any shared libraries</a> while others consider it a <a href="http://www.grahamlea.com/2016/04/shared-libraries-in-microservices-bad-advice/">bad advice</a>. There is plenty of discussion on the topic to sway you one way or another.</div>
<div>
<br /></div>
<div>
Here is my take on it </div>
<div>
<br /></div>
<div>
Using shared libraries between Microservices is fine as long as the following conditions are met:<br />
<br />
<b>1. Not Domain Specific:</b><br />
The common library should NOT contain any domain specific code. In other words, it is Ok, if you are using code libraries to reuse technical code e.g. a common code to make http requests. If you think about it, we do it already by using platform libraries for doing things like database connectivity, file I/O, etc. However, if your shared library contains some business logic than it will cause problems. Remember microservices use bounded context.<br />
<b><br /></b>
<b>2. Use Package Manager:</b><br />
The shared libraries must be published and consumed using a package manager. Package managers such as NPM or NuGet ensures that dependencies are on a particular version of library. It means that when a later newer of library is published, the consumer service don't need to update unless it wants to use functionality in the newer version.<br />
<b><br /></b>
<b>3. </b><b>Small library with Single Responsibility:</b><br />
Over years I have seen the menace of "core" or "common" libraries where any reusable code segment is put in a single common library. The problem with that is that the common library touches far too many points and is always required to be updated. For shared libraries between microservices, the shared libraries must be small and do a single job.<br />
<b><br /></b>
<b>4. </b><b>Avoid Transitive Dependencies:</b><br />
If your common library is dependent upon another library, which in turn is dependent upon another library, it would create dependencies, which are difficult to map and maintain. In this case, I would say that it's better to duplicate the code than to create a dependency hell of using inter-dependent libraries.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Ownership Dilemma</h3>
<div>
As I mentioned, microservices are fully autonomous. The team who create them own them and are responsible for everything about them from the choice of technology to deployment and release process. What about shared libraries? If "Team A" creates a shared library that is used by "Team B", which team would own the library? This is indeed a dilemma, especially in cases where the shared library needs to be modified. If Team B needs the change to be made, would they make the change or whether they would ask Team A to make the change. In either case, there is dependency between teams. </div>
<div>
<br /></div>
<div>
From experience, my stance on it is that the team who writes the shared library owns the library. If they require change in the library for their service, they should make the change. However, if other teams want changes in the shared library, they should rather drop the dependency, duplicate the code and do it within the service (or create another library). I agree that it cause effort duplication but in my experience it works much better than modifying the library for a purpose that it wasn't written for in the first place.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Then there is Composite</h3>
<div>
Microservices do not have dependencies on other microservices. To provide business value, there would be something the brings the services together. It could be a web page for a web application, web form for a windows app or an API gateway for public web APIs. Regardless of its type, it acts as composite. How are shared libraries resolved in a composite. As an example, let's say that services A and B both use CommonLib but Service A uses a different version of CommonLib. The answer to this question is within the definition of micro-service itself. Microservices are self-contained and would bring in an deploy everything that needs to run them themselves. Even if Service A and B are merely separate processes on the same machine, they would load different versions of common library in their process space. The composite does not need to worry about internal intricacies of services.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
And finally</h3>
<div>
As a disclaimer, this post is purely my own opinion based on my experience of working with microservices and in decomposing monoliths. If you follow these general guidance, you will find yourself with a large number of very small libraries, which are not changed very often. Both of these traits are good things. Also, from my experience it is easier to follow these rules if you are starting fresh. If you are starting with a huge monolith, you would most certainly start with a huge common library. Getting clarity on dependencies while working backwards need finer skills. </div>
<div>
<br /></div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com16tag:blogger.com,1999:blog-9108944828477346591.post-1956837971558392032018-07-25T20:52:00.001+01:002018-07-29T06:55:04.066+01:00Using Helm to deploy to a kubernetes cluster pulling images from a private container registry<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Background</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><a href="https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/" target="_blank">Kubernetes</a> is a great platform for deploying containerized applications. A typical production system makes use of a number of docker images. To run and monitor these images manually is complicated. Kubernetes takes care of this task. It starts up given number of containers, monitors the health of each container, creates new containers for each failing container and has a mechanism of managing secrets and configuration maps that can be used by container images. </span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Deployment to a Kubernetes cluster can be done by creating each k8s object manually using KubeCtl command line. This is of course not a feasible option for any production deployment. </span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">An alternate is to define all configuration in code by declaring them one or more YAML files. This is a better approach, however, it would still require that all kubernetes objects are created in order and doesn't scale well for larger of kubernetes objects.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Enter <a href="https://helm.sh/" target="_blank">Helm</a>. It is is a package manager for kubernetes and provides functionality to define, install, update and delete all kubernetes objects in your application with a single command.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">This this post, I will explain how to deploy a simple kubernetes application. I will use docker images that reside in a private dockerhub repository, To authenticate with dockerhub, my credentials will be stored in kubernetes secret.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Example Application Blueprint</span></h2>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We will deploy simple Node-Js web application called tiresias. It's stored in a private Dockerhub repository. Our deployment will include </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">1) A Kubernetes deployment with a replica-set of 2 (two instances of our containers will be running). </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">2) In addition, it would include Kubernetes Service to provide a single endpoint, and </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">3) An Ingress object to allow external traffic to hit the Service endpoint. </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Our YAML file look as below</span></div>
<div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<span style="font-family: "courier new" , "courier";"></span>
<br />
<pre><span style="font-family: "courier new" , "courier";">apiVersion: v1
kind: Service
metadata:
name: tiresias
labels:
app: tiresias
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: port-80
selector:
app: tiresias
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tiresias
spec:
replicas: 2
template:
metadata:
labels:
app: tiresias
spec:
containers:
- name: private-reg-container
image: tiresias/web:latest
ports:
- containerPort: 8080
imagePullSecrets:
- name: regcred
selector:
matchLabels:
app: tiresias
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tiresius
spec:
backend:
serviceName: tiresias
servicePort: 80
</span></pre>
</div>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span><span style="font-family: "arial" , "helvetica" , sans-serif;">Before running the above yaml with <i>KubeCtl apply -f</i> command, we need to create the secret named regcred. Now this is rather simple application but the dependency on secret is an indication on how dependencies between kubernetes objects make working only with "kubectl apply -f" a nightmare.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Creating our Helm Chart</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We create a new Helm chart called "auto-deploy" by executing </span><br />
<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">helm create auto-deploy</span></blockquote>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">This creates a new "auto-deploy" folder with the following contents</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-vRttm0OACK0/W1i07KSErcI/AAAAAAABxGI/cjtBn8jXFNklHiOgsp5XNBeh8Dz3lVPJQCLcBGAs/s1600/folder-content.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="133" data-original-width="426" height="199" src="https://1.bp.blogspot.com/-vRttm0OACK0/W1i07KSErcI/AAAAAAABxGI/cjtBn8jXFNklHiOgsp5XNBeh8Dz3lVPJQCLcBGAs/s640/folder-content.jpg" width="640" /></a></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span><span style="font-family: "arial" , "helvetica" , sans-serif;">Lets discuss each file and folder</span><br />
<ul style="text-align: left;">
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Chart.yaml -</b> Contain information about Helm chart such as Api Version, Name, Description, etc. </span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>values.yaml -</b> Contains values of each of the variables that are used in template</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Charts/ - </b>Contains charts onto which the current chart is dependent upon.</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>templates/ </b>Contains templates for deployment, which along with values.yaml file, generates Kubernetes configuration.</span></li>
</ul>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We delete the contents of the templates directory to create our objects from scratch.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Charts.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Before we start creating templates, lets set the information of our chart by setting the content to following text</span></div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "courier new" , "courier";">
apiVersion: v1
appVersion: "1.0"
description: A Helm Chart for Tiresias
name: auto-deploy
version: 0.1.0
</span></pre>
</div>
</div>
<br />
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">values.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">For our simplistic application, the only variables we need are related to authentication with our private repository in dockerhub. The contents of our values.yaml file is shown below</span></div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "courier new" , "courier";">
imageCredentials:
name: dockerhub
registry: https://index.docker.io/v1/
username: TODO
password: TODO
</span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We have four items in the file. We set the name, image registry URL, user name and password. We do not want to put our username / password in the text file, so the variables are just a place holder. We will pass the values as command line arguments.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Working with Secret</span></h3>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"></span><br />
<div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">To be able to download image from our private image repo, we would need to set up a secret of type kubernetes.io/dockerconfigjson. </span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">Kubernetes stores the secrets in a base64 encoded string, so we would need base64 encode our username / password. We will write a function for it. </span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">We create two files in the templates directory</span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">1) secret.yaml</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;">2) imagePullSecret.yaml</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
</span></h3>
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
secret.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The contents of secret.yaml is as below</span></div>
<div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.imageCredentials.name }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}
</span></span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">Lets dissect the contents a bit. </span><span style="font-family: "arial" , "helvetica" , sans-serif;">We are creating a simple Secret object. The name is set using to the value of the variable imageCredentials.name. The data is set using the template imagePullSecret, which is detailed as below.</span></span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
</div>
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">imagePullSecret.yaml</span></span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">In the the template file imagePullSecret.yaml, we define a function that spits out the base64 encoded string for username and password. The contents of the file is the text below</span><br />
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">
{{- define "imagePullSecret" }}
{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" </span></span></pre>
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">.Values.imageCredentials.registry (printf "%s:%s" </span></span></pre>
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">.Values.imageCredentials.username </span></span></pre>
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">.Values.imageCredentials.password | b64enc) | b64enc }}
{{- end }}
</span></span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;">The method is using values from variables imageCredentials.registry, imageCredentials.username and imageCredentials.password and using b64enc method to generate a base64 encoded string and printing it out.</span></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></span>
</div>
<div>
<div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</div>
<h3 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Deployment, Service and Ingress</span></h3>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now that secret is configured correctly, the next task is to set up our other three kubernetes objects i.e. Deployment, Service and Ingress. The definition for our objects is same as defined in the YAML file above except that we break it down in three different yaml files</span></div>
<div>
<div>
<div>
<br /></div>
<h4>
<span style="font-family: "arial" , "helvetica" , sans-serif;">deployment.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The content of deployment.yaml file is below. Note the use of variable to reference the name of image pull secret.</span></div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "courier new" , "courier";">apiVersion: apps/v1
kind: Deployment
metadata:
name: tiresias
spec:
replicas: 2
selector:
matchLabels:
app: tiresias
template:
metadata:
labels:
app: tiresias
spec:
containers:
- name: tiresias-dockerhub
image: tiresias/web:latest
ports:
- containerPort: 8080
imagePullSecrets:
- name: {{ .Values.imageCredentials.name }}<span style="font-family: "times new roman";">
</span></span></pre>
</div>
<div>
<br /></div>
<h4>
<span style="font-family: "arial" , "helvetica" , sans-serif;">service.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The content of service.yaml file is as below.</span></div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">apiVersion: v1
kind: Service
metadata:
name: tiresias-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: port-80
selector:
app: tiresias<span style="font-family: "times new roman";">
</span></span></span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<h4>
<span style="font-family: "arial" , "helvetica" , sans-serif;">ingress.yaml</span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The content of ingress.yaml file is as below. </span></div>
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tiresius
annotation:
http.port: "443"
spec:
backend:
serviceName: tiresias-svc
servicePort: 80<span style="font-family: "times new roman";">
</span></span></span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<div>
<br /></div>
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Executing our Helm chart</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Using helm to deploy kubernetes objects simply involves executing the helm install command. Since, we have a couple of variables where values need to be provided using command line arguments. Our command to execute our chart would look like below</span><br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">helm install ./auto-deploy --set imageCredentials.username=youruser<username>,imageCredentials.password=yourpassword<password></password></username></span></blockquote>
<span style="font-family: "arial" , "helvetica" , sans-serif;">As you can see, it is a breeze to work with. The output looks some like as follow</span><span style="font-family: "arial" , "helvetica" , sans-serif;"> </span><br />
<div style="background-color: #eeeeee; padding-left: 10px;">
<pre><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier";">NAME: veering-panther
LAST DEPLOYED: Wed Jul 25 12:32:22 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Secret
NAME TYPE DATA AGE
dockerhub kubernetes.io/dockerconfigjson 1 0s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
tiresias-svc ClusterIP 10.106.163.221 <none> 80/TCP 0s
==> v1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
tiresias 2 0 0 0 0s
==> v1beta1/Ingress
NAME HOSTS ADDRESS PORTS AGE
tiresius * 80 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
tiresias-646dbdc7b9-b9p7z 0/1 Pending 0 0s
tiresias-646dbdc7b9-h8w2k 0/1 Pending 0 0s<span style="font-family: "times new roman";">
</span></none></span></span></pre>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<div>
<br /></div>
</div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">You can see that a couple of kubernetes pods are set up and are in pending state right now. If you want to see current installation, you can run the following command</span><br />
<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">helm list</span></blockquote>
</div>
</div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The output looks like following</span><br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">NAME REVISION UPDATED STATUS CHART NAMESPACE</span><span style="font-family: "courier new" , "courier" , monospace;">veering-panther 1 Wed Jul 25 12:32:22 2018 DEPLOYED auto-deploy-0.1.0 default</span></blockquote>
<br />
<span style="font-family: "arial" , "helvetica" , sans-serif;">Note the name given by helm to your installation. If you want to delete all kubernetes objects, the simple helm command to do it is </span><br />
<div>
<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">helm delete veering-panther</span></blockquote>
</div>
<h2 style="text-align: left;">
Conclusion</h2>
<div>
<br />
<span style="font-family: "arial" , "helvetica" , sans-serif;">In this post, I explained using Helm to deploy a simple containerized application. For any production application of any size, using a simple helm command is much simpler and easier than running a series of kubectl commands. I hope you find this post useful. Please feel free to leave your comments / feedback.</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com9tag:blogger.com,1999:blog-9108944828477346591.post-91468659147697067222018-07-17T02:16:00.001+01:002018-07-25T00:34:16.384+01:00Using VSTS Release Management to create a Continuous Delivery pipeline for a Kubernetes service<div dir="ltr" style="text-align: left;" trbidi="on">
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Introduction</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">My <a href="https://hamidshahid.blogspot.com/2018/07/deploying-your-dockerized-application.html" target="_blank">last blog post</a> was about deploying my dockerized web application to Azure Kubernetes Service. In that post, I explained how to manually deploy docker images to a Kubernetes cluster on AKS. Though deploying manually might be a fulfilling experience, it is hardly something that we would do for an actual production system. Rather, we would want to create a deployment pipeline that automates the entire process.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">In this blog post, we would create a Release using <a href="https://docs.microsoft.com/en-us/vsts/pipelines/release/what-is-release-management?view=vsts" target="_blank">VSTS Release Management </a>and a deployment pipeline for deploying to a Kubernetes cluster on AKS.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Creating a Release</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The first step is to create a new release. From the "Build and release" menu on your VSTS project, click on the tab "Releases". Click the + button and select "Create release definition".</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-r948X5gd0TE/W0086T7OhEI/AAAAAAABxDg/UOWfS1yyedUlSeUpyZ4VWEx7q9AxA3lZgCLcBGAs/s1600/create_release_definition.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="188" data-original-width="585" height="203" src="https://1.bp.blogspot.com/-r948X5gd0TE/W0086T7OhEI/AAAAAAABxDg/UOWfS1yyedUlSeUpyZ4VWEx7q9AxA3lZgCLcBGAs/s640/create_release_definition.jpg" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We are taken to the release definition page. From the template, select "Deploy to a Kubernetes Cluster".</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-69JrY7I-XCY/W009JKp3_tI/AAAAAAABxDk/keReCzHlrb41zBWv4zdqZrB8vruniA7ZACLcBGAs/s1600/deply-to-kubernetes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="82" data-original-width="472" height="110" src="https://2.bp.blogspot.com/-69JrY7I-XCY/W009JKp3_tI/AAAAAAABxDk/keReCzHlrb41zBWv4zdqZrB8vruniA7ZACLcBGAs/s640/deply-to-kubernetes.png" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">For the names, we named our environment "dev" and gave our release the name of "DevRelease". Our deployment pipeline at this stage looks like a blank slate as shown below</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-cN-nAMDkcCw/W009Z-WlZSI/AAAAAAABxDs/p0MAJkaLRFM1CYnsMBS6wVa_h1jHwL4KgCLcBGAs/s1600/First-view.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="438" data-original-width="955" height="291" src="https://2.bp.blogspot.com/-cN-nAMDkcCw/W009Z-WlZSI/AAAAAAABxDs/p0MAJkaLRFM1CYnsMBS6wVa_h1jHwL4KgCLcBGAs/s640/First-view.PNG" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Selecting Artifacts</span></h2>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The input to our deployment pipeline are two artifacts</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
1) The docker image of our web application. </span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">This is produced by our VSTS builds and described in the post "Creating CI and Deployment builds for docker images". Our build pushed our images out to docker hub, so we will set up our pipeline to download it from there.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif; white-space: pre;"> </span></div>
<h4 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
2) A YAML file defining our Kubernetes service. </span></h4>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Our YAML file looks like following and is already described in the post "Deploying your dockerized application to Azure Kubernetes Service". We saved it in a oneservice.yaml file and committed it to our source git repository.</span><br />
<br /></div>
<div style="background-color: #eeeeee; padding-left: 20px;">
<pre><span style="font-family: "courier new" , "courier" , monospace;">apiVersion: v1
kind: Service
metadata:
name: aspnetvuejs
labels:
app: asapnetvuejs
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: port-80
selector:
app: aspnetvuejs
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: aspnetvuejs
spec:
replicas: 2
template:
metadata:
labels:
app: web
spec:
containers:
- name: private-reg-container
image: aspnetvuejs/web:latest
ports:
- containerPort: 8080
imagePullSecrets:
- name: docker-registry</span>
</pre>
</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">To select the docker image artifact, click on the "+ Add" button in the Artifacts section. From the list, click option "Docker Hub".</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-3ocmRcUpm4w/W00_yKrwsOI/AAAAAAABxD4/hozVrIlEtzERUZLdDGuZaVTF1PQCzZ6DgCLcBGAs/s1600/select%2Bdockerhub.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="319" data-original-width="574" height="352" src="https://1.bp.blogspot.com/-3ocmRcUpm4w/W00_yKrwsOI/AAAAAAABxD4/hozVrIlEtzERUZLdDGuZaVTF1PQCzZ6DgCLcBGAs/s640/select%2Bdockerhub.png" width="640" /></span></a></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">From the list of options, select the service endpoint for your Docker Hub repository. If it's not already set up, click on the Manage button to select it. Select the Namespace and Repository and give your source an alias. Click the Add button add your artifact.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Your application is how shown in list of Artifacts.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now, we need to add the YAML flie containing definition of our service. To do this, click the "+ Add" button in the Artifacts section again. This time, select option "Git". From the options displayed, select your team project and the Git repository. For branch, we selected branch "master" and for version selected latest for default version. Our final selection for Git looks as following</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-p1LNbsbttdc/W01ADednp0I/AAAAAAABxEA/nE2Xamr9CHgyFpQ3ihdTM3tzy7hhOcOHgCLcBGAs/s1600/select%2Bgit.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="842" data-original-width="632" height="640" src="https://4.bp.blogspot.com/-p1LNbsbttdc/W01ADednp0I/AAAAAAABxEA/nE2Xamr9CHgyFpQ3ihdTM3tzy7hhOcOHgCLcBGAs/s640/select%2Bgit.PNG" width="480" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Click
on the Add button to add the artifact. Now both the input artifact to our
deployment pipeline is setup.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-oeDw0pOB5iM/W01AbHzK8DI/AAAAAAABxEI/2U7EXAyJdoE2yIQkF0k9f5oEAxWeub6uQCLcBGAs/s1600/input-artifacts.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="467" data-original-width="671" height="444" src="https://3.bp.blogspot.com/-oeDw0pOB5iM/W01AbHzK8DI/AAAAAAABxEI/2U7EXAyJdoE2yIQkF0k9f5oEAxWeub6uQCLcBGAs/s640/input-artifacts.PNG" width="640" /></span></a></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<h2 style="clear: both; text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Setting Deployment</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The next step is to configure our deployment process. Since we selected template "Deploy to a Kubernetes cluster", we already have a task setup in our deployment process as shown by the "1 phase, 1 task" link. Click on the link to view details of the deployment process. We have one task in place to run kubectl apply.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-JdVE5xe2bLo/W01Al8EA54I/AAAAAAABxEM/mFR7O4oZPgg4jhHLjuvLgNLS4Jm7GL8SACLcBGAs/s1600/kubectl%2Bapply.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="102" data-original-width="628" height="102" src="https://1.bp.blogspot.com/-JdVE5xe2bLo/W01Al8EA54I/AAAAAAABxEM/mFR7O4oZPgg4jhHLjuvLgNLS4Jm7GL8SACLcBGAs/s640/kubectl%2Bapply.png" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">For the task arguments, we need to provide a Kubernetes Service connection. To do this, we clicked on the + New button</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ZN87v14AsaY/W01AvQWf7eI/AAAAAAABxEU/su0T2YAqnfc2Oqesys4QvIp2xnJC7lowQCLcBGAs/s1600/Kubernetes%2BService%2Bconnection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="69" data-original-width="1027" height="26" src="https://1.bp.blogspot.com/-ZN87v14AsaY/W01AvQWf7eI/AAAAAAABxEU/su0T2YAqnfc2Oqesys4QvIp2xnJC7lowQCLcBGAs/s400/Kubernetes%2BService%2Bconnection.png" width="400" /></span></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">1) We give our connection the name "dev".</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">2) Set the Server URL. You can find it in the Azure portal by visiting the properties page of your Kubernetes service on Azure portal</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ajkDnmRHoSA/W01BKBDLSLI/AAAAAAABxEg/itg2LRHolekU7Rv8qopSyoNjZhC0x6eGgCLcBGAs/s1600/kub%2Baddress.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="85" data-original-width="1113" height="48" src="https://3.bp.blogspot.com/-ajkDnmRHoSA/W01BKBDLSLI/AAAAAAABxEg/itg2LRHolekU7Rv8qopSyoNjZhC0x6eGgCLcBGAs/s640/kub%2Baddress.png" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">3) To
get your KubeConfig details, type in the following on your Cloud shell session</span></div>
</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">az
aks get-credentials --resource-group kubedemo --name kubedemo</span></blockquote>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Your
configurations are copied to the .kube/config file. Copy the contents of the
file and past it in your connection string.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">4) Select
option "Accept Untrusted Certificate".</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">5) Click
on Verify Connection to verify your connection. The deployment task is now
ready to deploy to our Kubernetes cluster.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ayE-v41cXWk/W01Bmb1MubI/AAAAAAABxEo/9H5hKtY5B9g7aVeMrJv7D9Vs8t-m8C6bQCLcBGAs/s1600/kubeconnection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="429" data-original-width="605" height="452" src="https://1.bp.blogspot.com/-ayE-v41cXWk/W01Bmb1MubI/AAAAAAABxEo/9H5hKtY5B9g7aVeMrJv7D9Vs8t-m8C6bQCLcBGAs/s640/kubeconnection.png" width="640" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We will keep our kubectl command as apply and select the "Use configuration files" checkbox so that we can pass the YAML file from our Git repository.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">For the Configuration file option, click on the ellipses button. From the Linked Artifacts, select the Git Artifact and select the YAML file we pushed out to our Git source code repository earlier. Click OK and our deployment process is ready for action.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The final and most important step is to Save our build release definition by clicking on the save button.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Running Deployment</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">At this stage, we have setup a build but it hasn't been deployed to an environment yet. To start a deployment, go back to the Release definitions view by clicking on Release link. Our new release is shown in the list, To deploy, we click on the ellipses with the release and click on "+ Release". A dialog appears asking about target environment and version for our artifacts. Selected the environment "dev" and the latest commit on our git repository. Click on the Create button to deploy to our Kubernetes Service.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-IqMAC52OTqs/W01CBASTEMI/AAAAAAABxE0/cCuGnhdvca4OHoPHQQhpZr279U4mmm2rgCLcBGAs/s1600/release%2Bdeployment.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="1063" data-original-width="634" src="https://1.bp.blogspot.com/-IqMAC52OTqs/W01CBASTEMI/AAAAAAABxE0/cCuGnhdvca4OHoPHQQhpZr279U4mmm2rgCLcBGAs/s1600/release%2Bdeployment.png" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Conclusion</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">This is the final post of my series of posts about creating docker images and deploy them to a Kubernetes cluster in Azure Cluster Service. In the series of posts, I explained</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<ol style="text-align: left;">
<li><a href="https://hamidshahid.blogspot.com/2018/06/docker-aspnet-core-nodejs.html" target="_blank"><span style="font-family: "arial" , "helvetica" , sans-serif;">How to run a simple ASPNet/JavaScript based website on docker.</span></a></li>
<li><a href="https://hamidshahid.blogspot.com/2018/07/creating-ci-and-deployment-builds-for.html" target="_blank"><span style="font-family: "arial" , "helvetica" , sans-serif;">Setup a CI process by creating build definition for continuous integration and deployable builds using VSTS.</span></a></li>
<li><a href="https://hamidshahid.blogspot.com/2018/07/deploying-your-dockerized-application.html" target="_blank"><span style="font-family: "arial" , "helvetica" , sans-serif;">Deploy our dockerized application as a Kubernetes Service on Azure Kubernetes Service, and finally.</span></a></li>
<li><a href="https://hamidshahid.blogspot.com/2018/07/using-vsts-release-management-to-create_17.html" target="_blank"><span style="font-family: "arial" , "helvetica" , sans-serif;">Setup a CD process by creating a release and a deployment process using VSTS.</span></a></li>
</ol>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com2tag:blogger.com,1999:blog-9108944828477346591.post-70426559101258799872018-07-16T07:43:00.005+01:002018-07-16T07:43:55.726+01:00Deploying your dockerized application to Azure Kubernetes Service<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Introduction</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">My last two blog posts were about creating docker images for an ASPNet/Javascript web application. In the <a href="https://hamidshahid.blogspot.com/2018/06/docker-aspnet-core-nodejs.html" target="_blank">first post<span id="goog_981679777"></span><span id="goog_981679778"></span>,</a> I described considerations to produce an optimized image. The <a href="https://hamidshahid.blogspot.com/2018/07/creating-ci-and-deployment-builds-for.html" target="_blank">second post</a> was about creating a CI/CD process for producing docker images.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Though producing nice lean docker images is good karma, they need to be deployed to a container orchestration system to be deployed in a productionized environment.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The three most popular container orchestration system are <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>, <a href="https://mesosphere.com/" target="_blank">Mesosphere</a> and <a href="https://docs.docker.com/engine/swarm/" target="_blank">Docker Swarm</a>. Of these three, Kubernetes is arguably the most popular and we are going to use it to run our container. Running your own kubernetes cluster in a production environment is ostensible. Luckily all major cloud vendor provide kubernetes as a service e.g. Google Cloud has a <a href="https://cloud.google.com/kubernetes-engine/" target="_blank">Google Kubernetes Engine</a>, Amazon AWS provides the <a href="https://aws.amazon.com/eks/" target="_blank">Elastic Container Service for Kubernetes (Amazon EKS)</a> and Microsoft Azure has the <a href="https://azure.microsoft.com/en-us/services/kubernetes-service/" target="_blank">Azure Kubernetes Service (AKS)</a>. We are going to use Azure Kubernetes Service to run our containers.</span><br />
<br />
<h3 style="text-align: left;">
</h3>
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Provisioning a Azure Kubernetes Service</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">1) Log on to the azure portal https://portal.azure.com. In the search text box, type in "Kubernetes". From the result listed, click on Kubernetes Services to see the list of kubernetes services.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-4XzlLvnp1aw/W0ud-jF_bLI/AAAAAAABwu4/nBmeLUu0TRwFYRslAr2f-qKzjn0_s-ZdQCLcBGAs/s1600/Kubernetes%2BService.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="321" data-original-width="606" height="169" src="https://4.bp.blogspot.com/-4XzlLvnp1aw/W0ud-jF_bLI/AAAAAAABwu4/nBmeLUu0TRwFYRslAr2f-qKzjn0_s-ZdQCLcBGAs/s320/Kubernetes%2BService.JPG" width="320" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">2) Click on the Add button to create a new kubernetes service. We are just going to use default options. We filled in details of your kubernetes cluster and click on the button Review + create button. </span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-4eudspj4444/W0ulCepoJdI/AAAAAAABwvE/5aROzwpNVZME0oCwx7lBTGWc-oDa37OWgCLcBGAs/s1600/createAKS.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="1187" data-original-width="1154" height="400" src="https://4.bp.blogspot.com/-4eudspj4444/W0ulCepoJdI/AAAAAAABwvE/5aROzwpNVZME0oCwx7lBTGWc-oDa37OWgCLcBGAs/s400/createAKS.JPG" width="387" /></span></a></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Review the settings and click the Create button to create your AKS. It takes a few minutes to get a fully configured AKS to be set up.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Once the cluster is set up, you can view the kubernetes dashboard by clicking on "View Kubernetes dashboard" link and follow the steps in the page displayed. If you are not familiar with <a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/" target="_blank">Kubernetes dashboard</a>, it is a web based interface that displays all kubernetes resources.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<br /></div>
<div>
<h2 style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">
Create a Kubernetes Secret</span></h2>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">To enable our Kubernetes cluster to download images from a private DockerHub repository, we will set up a Kubernetes secret containing credentials for our DockerHub repo. To do this, click on the ">_" button to open a Cloud Shell session.</span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">To create the secret run the following command</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div style="background-color: #eeeeee;">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">kubectl create secret docker-registry dockerhubcred --docker-server=https://index.docker.io/v1/ --docker-username=yourusernamee --docker-password=yourpassword --docker-email=youremailaddress</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<br />
<span style="font-family: arial, helvetica, sans-serif;">We can check the existence of the new secret by executing </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">kubectl get secret</span></blockquote>
<br />
<div>
<h2 style="text-align: left;">
<span style="font-family: arial, helvetica, sans-serif;">Create a Kubernetes Service</span></h2>
</div>
</div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">In Kubernetes, Containers are "housed" in Pods, However, pods are mortal and can be recreated at any time. Therefore, the end points of containers are exposed through an abstraction called "Kubernetes Service". </span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">Creation of kubernetes Service is a two stage process </span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">1) Create a Kubernetes Deployment: A deployment specifies a template, which include details of docker image, port, etc., replication details i.e. how many pods would be deployed as well as metadata that contains information about how pods would be selected.</span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">2) Create a Kubernetes Service. A service is deployed by specifying the selection criteria, port to be exported and service type. </span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">To perform the above-mentioned steps, we created the following yaml file. </span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<div style="background-color: #eeeeee;">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: courier new, courier, monospace;">apiVersion: v1</span><br />
<span style="font-family: courier new, courier, monospace;">kind: Service</span><br />
<span style="font-family: courier new, courier, monospace;">metadata:</span><br />
<span style="font-family: courier new, courier, monospace;"> name: aspnetvuejs</span><br />
<span style="font-family: courier new, courier, monospace;"> labels:</span><br />
<span style="font-family: courier new, courier, monospace;"> app: </span><span style="font-family: "courier new", courier, monospace;">asapnetvuejs</span><br />
<span style="font-family: courier new, courier, monospace;">spec:</span><br />
<span style="font-family: courier new, courier, monospace;"> type: LoadBalancer</span><br />
<span style="font-family: courier new, courier, monospace;"> ports:</span><br />
<span style="font-family: courier new, courier, monospace;"> - port: 80</span><br />
<span style="font-family: courier new, courier, monospace;"> targetPort: 8080</span><br />
<span style="font-family: courier new, courier, monospace;"> protocol: TCP</span><br />
<span style="font-family: courier new, courier, monospace;"> name: port-80</span><br />
<span style="font-family: courier new, courier, monospace;"> selector:</span><br />
<span style="font-family: courier new, courier, monospace;"> app: </span><span style="font-family: "courier new", courier, monospace;">aspnetvuejs</span><br />
<span style="font-family: courier new, courier, monospace;">---</span><br />
<span style="font-family: courier new, courier, monospace;">apiVersion: apps/v1beta1</span><br />
<span style="font-family: courier new, courier, monospace;">kind: Deployment</span><br />
<span style="font-family: courier new, courier, monospace;">metadata:</span><br />
<span style="font-family: courier new, courier, monospace;"> name: </span><span style="font-family: "courier new", courier, monospace;">aspnetvuejs</span><br />
<span style="font-family: courier new, courier, monospace;">spec:</span><br />
<span style="font-family: courier new, courier, monospace;"> replicas: 2</span><br />
<span style="font-family: courier new, courier, monospace;"> template:</span><br />
<span style="font-family: courier new, courier, monospace;"> metadata:</span><br />
<span style="font-family: courier new, courier, monospace;"> labels:</span><br />
<span style="font-family: courier new, courier, monospace;"> app: web</span><br />
<span style="font-family: courier new, courier, monospace;"> spec:</span><br />
<span style="font-family: courier new, courier, monospace;"> containers:</span><br />
<span style="font-family: courier new, courier, monospace;"> - name: private-reg-container</span><br />
<span style="font-family: courier new, courier, monospace;"> image: aspnetvuejs/web:latest</span><br />
<span style="font-family: courier new, courier, monospace;"> ports:</span><br />
<span style="font-family: courier new, courier, monospace;"> - containerPort: 8080</span><br />
<span style="font-family: courier new, courier, monospace;"> imagePullSecrets:</span><br />
<span style="font-family: courier new, courier, monospace;"> - name: docker-registry</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
</div>
</div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">To run the yaml file, I executed the following series of command on my Cloud Shell session</span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">1) Create the yaml file by executing </span></div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">vim oneservice.yaml</span></blockquote>
<div>
<span style="font-family: arial, helvetica, sans-serif;">Copy the yaml content above and save the file.</span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">2) Create the service and deployed by running</span></div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">kubectl apply -f oneservice.yaml</span></blockquote>
<br />
<div>
<span style="font-family: arial, helvetica, sans-serif;">The service and deployment is now created. To view your service, I typeed in the following</span></div>
<div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">kubectl get service</span></blockquote>
<br />
<div>
<span style="font-family: arial, helvetica, sans-serif;">The public IP address of the service is displayed in the list of service shown in the result. The deployed web web application can be viewed by typing in the public Ip address.</span> </div>
</div>
<div>
<span style="font-family: arial, helvetica, sans-serif;"><br /></span></div>
<br />
<h3 style="text-align: left;">
</h3>
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Conclusion and next steps</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: arial, helvetica, sans-serif;">In this post, we created a kubernetes service by declaring kubernetes objects in a yaml file and running them using Cloud Shell. This is useful in explaining the steps and understanding what is involved. However, in reality we would want to do it in a well-defined deployment process. In my next post, I will explain how to deploy to a kubernetes service using VSTS release management.</span></div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com1tag:blogger.com,1999:blog-9108944828477346591.post-37328605047410114332018-07-13T01:23:00.002+01:002018-07-14T20:54:34.993+01:00Creating CI and Deployment builds for docker images<div dir="ltr" style="text-align: left;" trbidi="on">
In my <a href="https://hamidshahid.blogspot.com/2018/06/docker-aspnet-core-nodejs.html" target="_blank">last blog post</a>, I wrote about steps to create a Docker container for running aspnet/Javascript Services. In the post, we created a Docker file to create a Docker image with a website running creating on VueJs and Asp.Net core.<br />
<br />
In real life, we would like our docker images to be pushed out to a container registry. We would also like it to be done through a team build. We would also want to have a CI build in place so every commit is vetted. This blog post details setting up the CI and deployment builds for producing docker images.<br />
<br />
<h3>
Dockerfile</h3>
<div>
To start with, lets review the docker file we created in the last post. I have modified it slightly to parameterized the exposed port<br />
<br />
<div style="background-color: #eeeeee; font-family: Courier New;">
<pre># Stage 1 - Restoring & Compiling
FROM microsoft/dotnet:2.1-sdk-alpine3.7 as builder
WORKDIR /source
RUN apk add --update nodejs nodejs-npm
COPY *.csproj .
RUN dotnet restore
COPY package.json .
RUN npm install
COPY . .
RUN dotnet publish -c Release -o /app/
# Stage 2 - Creating Image for compiled app
FROM microsoft/dotnet:2.1.1-aspnetcore-runtime-alpine3.7 as baseimage
ARG port=8080
RUN apk add --update nodejs nodejs-npm
WORKDIR /app
COPY --from=builder /app .
ENV ASPNETCORE_URLS=http://+:${port}
EXPOSE ${port}
CMD ["dotnet", "vue2spa.dll", "--server.urls", "http://+:${port}"]
</pre>
</div>
The docker file creates a docker image that can be pulled and deployed in any application. <br />
<br />
<h3>
Setting up the CI Build</h3>
<br />
<div>
We will use the new YAML build feature in VSTS to setup our CI/CD build. Our YAML CI build file would have a single step that would attempt to build the docker image. The publishing of website is performed within the docker build process as described in the Dockerfile above.
</div>
<br />
<div>
Our very simple YAML file looks like following <br />
<br /></div>
<div style="background-color: #eeeeee; font-family: Courier New;">
<pre>name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr)
steps:
- script: |
docker build --build-arg port=8080 --rm --compress -t sampleaspjs/web .
workingDirectory: $(Build.Repository.LocalPath)/web
displayName: Build Docker Image
</pre>
</div>
<div>
<br />
We named the yaml file as .vsts-ci.yml. Commit it and push it out to our git repository.<br />
<br />
Now that the yaml file is in our code repo, lets go and set up the CI build.<br />
<ol>
<li style="margin: 15px 0;">From the Build Definitions page, click on the "+ New" button to create a build definition.
<br /> <div class="separator" style="clear: both; text-align: left;">
<a href="https://4.bp.blogspot.com/-KB0hhkML7oA/W0fkvoJlFnI/AAAAAAABwt4/qxssrlHDlqY1bGU2ekUbEPiXoFMQDk4-gCLcBGAs/s1600/News.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="85" data-original-width="418" height="81" src="https://4.bp.blogspot.com/-KB0hhkML7oA/W0fkvoJlFnI/AAAAAAABwt4/qxssrlHDlqY1bGU2ekUbEPiXoFMQDk4-gCLcBGAs/s400/News.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0;">Select the source to VSTS Git, select your Team Project, repository and the default branch of master <div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-v_XFUvDE6E4/W0fopHhkRaI/AAAAAAABwuE/eeKebJA-Cz095H0WYiLrwKzTp_JsMuzCACLcBGAs/s1600/selectsource.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="515" data-original-width="842" height="244" src="https://2.bp.blogspot.com/-v_XFUvDE6E4/W0fopHhkRaI/AAAAAAABwuE/eeKebJA-Cz095H0WYiLrwKzTp_JsMuzCACLcBGAs/s400/selectsource.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0px;">From the list of template, select option YAML in the Configuration as Code classification and click Apply<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-QI-DlrApX7g/W0fqqvq5P1I/AAAAAAABwuQ/4DmMZnDrJPIl7bchmIWr4A5iAJJ8FkdIgCLcBGAs/s1600/configureAscode.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="1114" height="91" src="https://1.bp.blogspot.com/-QI-DlrApX7g/W0fqqvq5P1I/AAAAAAABwuQ/4DmMZnDrJPIl7bchmIWr4A5iAJJ8FkdIgCLcBGAs/s400/configureAscode.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0px;">In the build definition, type in the build name, select the "Hosted Linux Preview" queue and select the YAML path. Make sure Continuous Integration is enabled. Save the build definition.<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/--2Uh9SmrYsQ/W0frwmYrKdI/AAAAAAABwuY/s4Dwb6kQHaQ0zF_GVFkBzi6IT7ZGAyYbQCLcBGAs/s1600/ci.yaml.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="294" data-original-width="1600" height="117" src="https://4.bp.blogspot.com/--2Uh9SmrYsQ/W0frwmYrKdI/AAAAAAABwuY/s4Dwb6kQHaQ0zF_GVFkBzi6IT7ZGAyYbQCLcBGAs/s640/ci.yaml.PNG" width="640" /></a></div>
</li>
</ol>
<div style="text-align: center;">
<br /></div>
</div>
</div>
<div style="text-align: left;">
<div style="text-align: left;">
Now that we have the CI build, lets turn out attention to create a deployment deployment build</div>
<h3>
</h3>
<h3>
Setting up the product build</h3>
<br />
<div>
The steps to create our product build is similar to that of CI build except that we will have a different YAML file. Our build process will go one step further. In addition to creating the docker image, it would also push out docker images to dockerhub<br />
<br /></div>
<div>
Our YAML file looks like following<br />
<br /></div>
<div style="background-color: #eeeeee; font-family: "courier new";">
<pre>name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr)
steps:
- script: |
docker build --build-arg port=8080 --rm --compress -t tiresias/web .
workingDirectory: $(Build.Repository.LocalPath)/web
displayName: Build Docker Image
- script: |
docker tag aspnetvuejs/web tiresias/web:$(Build.BuildNumber)
workingDirectory: $(Build.Repository.LocalPath)/web
displayName: Tag docker image with buil d version
- script: |
docker login --username your-username --password your-password
workingDirectory: $(Build.Repository.LocalPath)/web
displayName: Docker Login
- script: |
docker push aspnetvuejs/web
workingDirectory: $(Build.Repository.LocalPath)/web
displayName: Docker Push
</pre>
</div>
<div>
<br />
We named the yaml file as .vsts-build.yml. Commit it and push it out to our git repository.<br />
<br />
The process to set up the deployment build is same as above.<br />
<ol>
<li style="margin: 15px 0px;">From the Build Definitions page, click on the "+ New" button to create a build definition.<br /><div class="separator" style="clear: both;">
<a href="https://4.bp.blogspot.com/-KB0hhkML7oA/W0fkvoJlFnI/AAAAAAABwt4/qxssrlHDlqY1bGU2ekUbEPiXoFMQDk4-gCLcBGAs/s1600/News.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="85" data-original-width="418" height="81" src="https://4.bp.blogspot.com/-KB0hhkML7oA/W0fkvoJlFnI/AAAAAAABwt4/qxssrlHDlqY1bGU2ekUbEPiXoFMQDk4-gCLcBGAs/s400/News.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0px;">Select the source to VSTS Git, select your Team Project, repository and the default branch of master <div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-v_XFUvDE6E4/W0fopHhkRaI/AAAAAAABwuE/eeKebJA-Cz095H0WYiLrwKzTp_JsMuzCACLcBGAs/s1600/selectsource.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="515" data-original-width="842" height="244" src="https://2.bp.blogspot.com/-v_XFUvDE6E4/W0fopHhkRaI/AAAAAAABwuE/eeKebJA-Cz095H0WYiLrwKzTp_JsMuzCACLcBGAs/s400/selectsource.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0px;">From the list of template, select option YAML in the Configuration as Code classification and click Apply<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-QI-DlrApX7g/W0fqqvq5P1I/AAAAAAABwuQ/4DmMZnDrJPIl7bchmIWr4A5iAJJ8FkdIgCLcBGAs/s1600/configureAscode.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="1114" height="91" src="https://1.bp.blogspot.com/-QI-DlrApX7g/W0fqqvq5P1I/AAAAAAABwuQ/4DmMZnDrJPIl7bchmIWr4A5iAJJ8FkdIgCLcBGAs/s400/configureAscode.PNG" width="400" /></a></div>
</li>
<li style="margin: 15px 0px;">In the build definition, type in the build name, select the "Hosted Linux Preview" queue and select the YAML path. Make sure Continuous Integration is enabled. Save the build definition.<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Exwf3j-3ID8/W0ft_jOyNaI/AAAAAAABwuk/ISpdkNUD5Yc0ZZbGosioDCxvy4h0QhZIgCLcBGAs/s1600/Deployment.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="338" data-original-width="1600" height="134" src="https://2.bp.blogspot.com/-Exwf3j-3ID8/W0ft_jOyNaI/AAAAAAABwuk/ISpdkNUD5Yc0ZZbGosioDCxvy4h0QhZIgCLcBGAs/s640/Deployment.PNG" width="640" /></a></div>
</li>
</ol>
<div style="text-align: center;">
<div style="text-align: left;">
With the product build in place, we have a mechanism of creating docker images and pushing it out to a container registry.</div>
<h3 style="text-align: left;">
</h3>
<h3 style="text-align: left;">
</h3>
<h3 style="text-align: left;">
Next steps</h3>
<div style="text-align: left;">
In this post, I described the steps to create a CI / build process for verifying, creating and distributing docker images. In my next post, I will deploy the docker images to a Kubernetes cluster created on Azure Kubernetes Service.</div>
<div style="text-align: left;">
<br /></div>
<div>
<br /></div>
</div>
</div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com1tag:blogger.com,1999:blog-9108944828477346591.post-44233768658503081332018-06-23T00:04:00.000+01:002018-07-13T16:48:16.617+01:00Running aspnet/JavaScriptServices on Docker<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
The <a href="https://github.com/aspnet/JavaScriptServices" style="font-family: verdana, sans-serif;" target="_blank">aspnet/JavaScriptServices</a><span style="font-family: "verdana" , sans-serif;"> project is part of ASP.NET Core and allows you to use Modern Web App frameworks such as Angular, React, etc. for creating web user interfaces of your application, alongside using ASP.NET Core for your web services. If you are using VueJS, the project </span><a href="https://github.com/MarkPieszak/aspnetcore-Vue-starter" style="font-family: verdana, sans-serif;" target="_blank">aspnetcore-Vue-starter</a><span style="font-family: "verdana" , sans-serif;"> does the same thing.</span></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Since, it uses both node.js and .net core, any server hosting the website would need to have both the technologies installed on it. In addition to that, your application might be using node modules and nuget packages, which would need to be deployed on to the server. </span><br />
<h2 style="text-align: left;">
</h2>
<h2 style="text-align: left;">
</h2>
<h3 style="text-align: left;">
Creating Docker Image</h3>
<span style="font-family: "verdana" , sans-serif;">When deploying your application in a docker container, typically you start with a base image. For instance, when deploying your .Net core application, your docker file would start with an instruction like </span><br />
<pre class="highlight" style="background-color: whitesmoke; border-radius: 4px; border: 1px solid rgb(204, 204, 204); box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13px; line-height: 1.42857; margin-bottom: 10px; overflow: auto; padding: 9.5px; word-break: normal !important; word-wrap: break-word;"><code style="background-color: transparent; border-radius: 0px; box-sizing: border-box; color: inherit; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; overflow-wrap: normal; overflow-x: auto; padding: 0px;"><span class="k" style="box-sizing: border-box; color: darkmagenta;">FROM</span><span class="s" style="box-sizing: border-box; color: #cd5555;"> microsoft/aspnetcore</span></code></pre>
<br />
<span style="font-family: "verdana" , sans-serif;">But if you are creating a node base application, your docker would start with an instruction like </span><br />
<pre class="highlight" style="background-color: whitesmoke; border-radius: 4px; border: 1px solid rgb(204, 204, 204); box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13px; line-height: 1.42857; margin-bottom: 10px; overflow: auto; padding: 9.5px; word-break: normal !important; word-wrap: break-word;"><code style="background-color: transparent; border-radius: 0px; box-sizing: border-box; color: inherit; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; overflow-wrap: normal; overflow-x: auto; padding: 0px;"><span class="k" style="box-sizing: border-box; color: darkmagenta;">FROM</span><span class="s" style="box-sizing: border-box; color: #cd5555;"> node</span></code></pre>
<div>
<code style="background-color: transparent; border-radius: 0px; box-sizing: border-box; color: inherit; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; overflow-wrap: normal; overflow-x: auto; padding: 0px;"><span class="s" style="box-sizing: border-box; color: #cd5555;"><span style="color: black; font-family: "times new roman";"><br /></span></span></code></div>
<div>
<span style="font-family: "verdana" , sans-serif;">So, what images should be used for the projects that use both node.js and .net core. The answer is to take one image and install the other library on it. We start with looking at the size of the image. The microsoft/aspnetcore is 345 MB and node:latest is 674MB. </span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-XO-Nqx0_G58/Wy17STf5YMI/AAAAAAABv54/Xo3jRqvreE80OCVkzvgqCuLvea_ZlcLgwCLcBGAs/s1600/2018-06-22_15-32-41.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="52" data-original-width="717" height="46" src="https://3.bp.blogspot.com/-XO-Nqx0_G58/Wy17STf5YMI/AAAAAAABv54/Xo3jRqvreE80OCVkzvgqCuLvea_ZlcLgwCLcBGAs/s640/2018-06-22_15-32-41.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
<span style="font-family: "verdana" , sans-serif;">Both are pretty huge right? So, the first step is to find the slimmer versions of the containers which can be used for production. I found out that there is an alpine image of node, which is about 70 MB that we can use as a baseline and installed .net core on top of it.</span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-pduljQh2WLQ/Wy19QecD0dI/AAAAAAABv6E/MoV3tlzdejYG9D_RpJbyRRg922B2Ed5iQCLcBGAs/s1600/alpine.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="20" data-original-width="737" height="17" src="https://2.bp.blogspot.com/-pduljQh2WLQ/Wy19QecD0dI/AAAAAAABv6E/MoV3tlzdejYG9D_RpJbyRRg922B2Ed5iQCLcBGAs/s640/alpine.jpg" width="640" /></a></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<span style="font-family: "verdana" , sans-serif;">We could do that but installing .net core on ubuntu takes far too many commands as compared to installing node.js on ubuntu. So an alternative would be to find an alpine image for dotnet core and install node on it. So, I look and yes there is certainly an alpine image for dotnet core and at about 162MB in size is just right for us.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-X3uJB1y8BiU/Wy1-MYxQ6PI/AAAAAAABv6M/nEQa-BE1kTgDyjwnIvZteU-4M2IZL7pvwCLcBGAs/s1600/dotnet%2Balpine%2Bimage.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="28" data-original-width="962" height="17" src="https://2.bp.blogspot.com/-X3uJB1y8BiU/Wy1-MYxQ6PI/AAAAAAABv6M/nEQa-BE1kTgDyjwnIvZteU-4M2IZL7pvwCLcBGAs/s640/dotnet%2Balpine%2Bimage.jpg" width="640" /></a></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">This would do the trick. So our dockerfile to deploy node on top of dotnet would look like this </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"></span><br />
<div style="background-color: whitesmoke; color: #333333; font-family: Consolas, "Courier New", monospace, "Segoe UI Emoji"; font-size: 14px; line-height: 19px; white-space: pre;">
<div>
<span style="color: #4b83cd;">FROM</span> microsoft/dotnet:2.1.1-aspnetcore-runtime-alpine3.7 <span style="color: #4b83cd;">as</span> baseimage</div>
<div>
<span style="color: #4b83cd;">RUN</span> apk add --update nodejs nodejs-npm</div>
<div>
<span style="color: #4b83cd;"></span></div>
</div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I actually added multi-stage build process in our docker file so that the compilation the application is compiled in it as well and a image produced at the end of the compilation process. The entire Dockerfile for the project produced by </span><a href="https://github.com/MarkPieszak/aspnetcore-Vue-starter" style="font-family: Verdana, sans-serif;" target="_blank">aspnetcore-Vue-starter</a><span style="font-family: "verdana" , sans-serif;"> project template looks follows</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"></span><br />
<div style="background-color: whitesmoke; color: #333333; font-family: Consolas, "Courier New", monospace, "Segoe UI Emoji"; font-size: 14px; line-height: 19px; white-space: pre;">
<div>
<span style="color: #aaaaaa; font-style: italic;"># Stage 1 - Restoring & Compiling</span></div>
<div>
<span style="color: #4b83cd;">FROM</span> microsoft/dotnet:2.1-sdk-alpine3.7 <span style="color: #4b83cd;">as</span> builder</div>
<div>
<span style="color: #4b83cd;">WORKDIR</span> /source</div>
<div>
<span style="color: #4b83cd;">RUN</span> apk add --update nodejs nodejs-npm</div>
<div>
<span style="color: #4b83cd;">COPY</span> *.csproj .</div>
<div>
<span style="color: #4b83cd;">RUN</span> dotnet restore</div>
<div>
<span style="color: #4b83cd;">COPY</span> package.json .</div>
<div>
<span style="color: #4b83cd;">RUN</span> npm install</div>
<div>
<span style="color: #4b83cd;">COPY</span> . .</div>
<div>
<span style="color: #4b83cd;">RUN</span> dotnet publish -c Release -o /app/</div>
<br />
<div>
<span style="color: #aaaaaa; font-style: italic;"># Stage 2 - Creating Image for compiled app</span></div>
<div>
<span style="color: #4b83cd;">FROM</span> microsoft/dotnet:2.1.1-aspnetcore-runtime-alpine3.7 <span style="color: #4b83cd;">as</span> baseimage</div>
<div>
<span style="color: #4b83cd;">RUN</span> apk add --update nodejs nodejs-npm</div>
<div>
<span style="color: #4b83cd;">WORKDIR</span> /app</div>
<div>
<span style="color: #4b83cd;">COPY</span> --from=builder /app .</div>
<div>
<span style="color: #4b83cd;">ENV</span> ASPNETCORE_URLS=http://+:$port</div>
<br />
<div>
<span style="color: #aaaaaa; font-style: italic;"># Run the application. REPLACE the name of dll with the name of the dll produced by your application</span></div>
<div>
<span style="color: #4b83cd;">EXPOSE</span> $port</div>
<div>
<span style="color: #4b83cd;">CMD</span> [<span style="color: #448c27;">"dotnet"</span>, <span style="color: #448c27;">"vue2spa.dll"</span>]</div>
</div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><b>PS: </b></span><span style="font-family: "verdana" , sans-serif;">In case, you want to use a base image with .net core 2.1 and node already on it, you can use the base image </span><span style="font-family: "verdana" , sans-serif;"><a href="https://hub.docker.com/r/hamidshahid/aspnetcore-plus-node/">https://hub.docker.com/r/hamidshahid/aspnetcore-plus-node/</a>. Simply pull it by calling </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">docker pull hamidshahid/aspnetcore-plus-node</span></blockquote>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com3tag:blogger.com,1999:blog-9108944828477346591.post-60680913479451104352018-03-29T00:56:00.000+01:002018-04-04T02:02:28.703+01:00VSTS / TFS 2018 Viewing test run history for a given test case <div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><b>UPDATE:</b><br /><b><a href="https://twitter.com/divyavaishnavi" target="_blank">Divya Vaishyani</a> from Visual Studio Team Services team has rightly pointed out that it is possible to view test result history for a test case using the Test results pane as documented <a href="https://docs.microsoft.com/en-us/vsts/release-notes/2014/nov-04-team-services#recent-test-results" target="_blank">here</a>. However, the test results from across Test Plans. This is quite confusing and different than the test history that showed. The workaround in this post allows you to view history for the same test plan</b></span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<div class="comment-timestamp" style="background-color: white; color: #202020; display: inline-block; font-family: arial, sans-serif; font-size: 13px; margin-bottom: 15px; padding: 0px;">
</div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">With the release of TFS 2018, running automated tests from Microsoft Test Manager (MTM) isn't supported any more. (see <a href="https://docs.microsoft.com/en-us/visualstudio/releasenotes/tfs2018-relnotes" target="_blank">TFS 2018 release notes</a>) doesn't support MTM to run automated tests from. This was <a href="https://blogs.msdn.microsoft.com/bharry/2016/04/13/team-services-and-tfs-roadmap-update-apr-2016/" target="_blank">announced</a> in VSTS and TFS road map about two years ago. </span></div>
<div style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">The test planning and management features in TFS / VSTS are pretty cool. However, there is one feature that I feel is rather missing and that is ability to view the history of a particular test case. In MTM, you could just click on the "View Results" link on a Test Case and view previous results. However, in VSTS, it is not possible to view test case's run history. There is a <a href="https://visualstudio.uservoice.com/forums/330519-visual-studio-team-services/suggestions/2037641-provide-more-visibility-for-a-test-case-s-result-s" target="_blank">feature request </a>in the user voice for it. Do remember to add your vote for it!!</span></div>
<div style="text-align: left;">
<br />
<span style="font-family: "arial" , "helvetica" , sans-serif;">1) In TFS, click on "Test" from the top menu and select the test suite where your test case is. Select the test case that you are interested in. Then Click pass or fail button. This will generate a manual test run for the given test.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://2.bp.blogspot.com/-faND6nHgzxQ/WrwpTTwyhcI/AAAAAAABsbM/-OvZsxwPPUsuO0sNVuSFJmEYlqufOnQkQCLcBGAs/s1600/FirstStep%2BManual%2BTest.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="367" data-original-width="1130" height="207" src="https://2.bp.blogspot.com/-faND6nHgzxQ/WrwpTTwyhcI/AAAAAAABsbM/-OvZsxwPPUsuO0sNVuSFJmEYlqufOnQkQCLcBGAs/s640/FirstStep%2BManual%2BTest.png" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8px;"><span style="font-family: "arial" , "helvetica" , sans-serif;">Trigger a Manual Run for your Test Case</span></td></tr>
</tbody></table>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">2) Go to MTM --> Test --> Analyze Test Run. Select option "Manual Runs" in the View option</span></div>
<div style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;">The good news is that there is a workaround - using MTM - for you to view history of test runs for a particular test case.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;"></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://4.bp.blogspot.com/-i1Lw7HNx5Hc/Wrwpebn0FfI/AAAAAAABsbQ/Uq5RgKGJ4cs60rOi3Ls6eBD574Wnm1H_ACLcBGAs/s1600/Step%2B2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="283" data-original-width="1600" height="113" src="https://4.bp.blogspot.com/-i1Lw7HNx5Hc/Wrwpebn0FfI/AAAAAAABsbQ/Uq5RgKGJ4cs60rOi3Ls6eBD574Wnm1H_ACLcBGAs/s640/Step%2B2.png" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8px;"><span style="font-family: "arial" , "helvetica" , sans-serif;">Find the Manaul Test Run in MTM</span></td></tr>
</tbody></table>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">3) Open the test run. Right click on test and click "View Results".</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://3.bp.blogspot.com/-h0IwOudWoLA/WrwpnUduBMI/AAAAAAABsbU/3zLdieZihHEL4X0GWYVRxifeBr28DVQ-wCLcBGAs/s1600/Step%2B3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="674" data-original-width="1600" height="269" src="https://3.bp.blogspot.com/-h0IwOudWoLA/WrwpnUduBMI/AAAAAAABsbU/3zLdieZihHEL4X0GWYVRxifeBr28DVQ-wCLcBGAs/s640/Step%2B3.png" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8px;"><span style="font-family: "arial" , "helvetica" , sans-serif;">View Test Run in MTM</span></td></tr>
</tbody></table>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">4) The list of results will show you the manual run as well as automated runs, which is what you are really looking for.</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://3.bp.blogspot.com/-fIk0sBsGoOQ/Wrwp0lfS_wI/AAAAAAABsbY/55Oq7HJeQok8LnVmTI--nIGNbhDtMoVyQCLcBGAs/s1600/Step%2B4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" data-original-height="1034" data-original-width="1600" height="413" src="https://3.bp.blogspot.com/-fIk0sBsGoOQ/Wrwp0lfS_wI/AAAAAAABsbY/55Oq7HJeQok8LnVmTI--nIGNbhDtMoVyQCLcBGAs/s640/Step%2B4.png" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8px;"><span style="font-family: "arial" , "helvetica" , sans-serif;">View Test Results</span></td></tr>
</tbody></table>
</div>
<div style="background-color: white; border: 0px; box-sizing: inherit; clear: both; font-stretch: inherit; line-height: inherit; margin-bottom: 1em; padding: 0px; text-align: left; vertical-align: baseline;">
<span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;"><br /></span></span><span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;">It's still a workaround and you still need MTM but you can see the history of test cases this way. I hope you find this post useful.</span></span><br />
<span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;"><br /></span></span>
<span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;"><br /></span></span>
<br />
<span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;"><br /></span></span>
<span style="color: #242729; font-family: "arial" , "helvetica" , sans-serif;"><span style="font-size: 15px;"><br /></span></span>
<br />
<div>
<br /></div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com6tag:blogger.com,1999:blog-9108944828477346591.post-1748981357933543492017-12-12T08:33:00.002+00:002017-12-12T08:34:36.906+00:00Creating a yaml CI Build for .net application<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">One of the great announcements from this year's <a href="https://www.microsoft.com/en-us/connectevent/">Microsoft Connect()</a> conference was YAML support for VSTS build
definitions. </span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">For
me, it's a great way forward towards "codifying" the build pipeline.
The current TFS builds technology, introduced in Team Foundation Server 2015,
despite all the benefits of a loose and extensible mechanism is rather
difficult to maintain as code and doesn't really fit "pipeline as a
code" definition. If you remember, earlier versions of Team Foundation
Server (TFS 2005 and TFS 2008) used an MSBuild file to run builds. Whilst this
was easy to code and maintain, extensibility was rather limited. Then, Team
Foundation Server 2010 introduced XAML builds with better support for workflows
but was difficult to work with. TFS 2015 simplifies XAML but the whole logic is
spread across different aspects of the build. Yaml solves this shortcoming
nicely.</span></div>
<h4 style="margin: 0in; text-align: left;">
</h4>
<div>
<br /></div>
<h4 style="margin: 0in; text-align: left;">
Enable YAML Builds Preview Feature</h4>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">At
the time of writing this post, support for YAML builds is still in preview in
VSTS. To enable it for your account, click on your profile and select option
"Preview Features" from the drop down menu</span></div>
<div style="margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-6A-v1_n-2aI/Wi-TAuLn7fI/AAAAAAABr7g/gsJk9hpz3C4by1Yv5k-tDSR2tMC-VF7vgCEwYBhgL/s1600/Preview%2Bfeatures.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="604" height="317" src="https://1.bp.blogspot.com/-6A-v1_n-2aI/Wi-TAuLn7fI/AAAAAAABr7g/gsJk9hpz3C4by1Yv5k-tDSR2tMC-VF7vgCEwYBhgL/s320/Preview%2Bfeatures.png" width="320" /></a></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Select
option "from this account [<span style="font-style: italic;">projectName</span>]".
Scroll down till you find the "Build Yaml definitions" feature and
set it to On</span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-wIzpugWv_h4/Wi-TfbbR2hI/AAAAAAABr7o/gB4fpppfGK8A4oUt9PZkcLgQqgNCuJeCACLcBGAs/s1600/Build%2BYaml%2Bdefinitions%2Bsupport.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="416" data-original-width="879" height="151" src="https://2.bp.blogspot.com/-wIzpugWv_h4/Wi-TfbbR2hI/AAAAAAABr7o/gB4fpppfGK8A4oUt9PZkcLgQqgNCuJeCACLcBGAs/s320/Build%2BYaml%2Bdefinitions%2Bsupport.png" width="320" /></a></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">We are now ready to use YAML builds.</span></div>
<div style="margin: 0in;">
<br /></div>
<h4 style="margin: 0in; text-align: left;">
<span style="font-family: "verdana" , sans-serif;">Creating a Yaml Build </span></h4>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">There
are two ways in which we can set up a Yaml build. </span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">1)
Create a file called .vsts-ci.yaml. When you push your change with this file to
TFS, a build definition, using this file is created for you.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">2)
Explicitly create a build definition using the YAML template, providing the
path of YAML file that you have committed in the repo.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">We
will go with option 2.</span></div>
<div style="margin: 0in;">
<br /></div>
<h4 style="margin: 0in; text-align: left;">
Create build.yml file</h4>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">YAML
file format is the format of choice for configuration files and is used by some
exciting technologies like docker, Ansible, etc. It's great that VSTS now
supports it as well. </span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">For
this demo, we are creating build for a simple .Net Web Application. For the
purpose of building our application, we need to do the following</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">1)
Restore all NuGet packages</span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">2)
Build the entire solution</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Our
very simple yaml file looks as below</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">steps</span><span style="color: #979797;">:</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #979797;">- </span><span style="color: #569cd6;">task</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">nugetrestore@1</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">displayName</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">NuGet Restore</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">inputs</span><span style="color: #979797;">:</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">projects</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">"MyWebApplication.sln"</span></span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #979797;">- </span><span style="color: #569cd6;">task</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">MSBuild@1</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">displayName</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">Building solution</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">inputs</span><span style="color: #979797;">: </span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">command</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">build</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><span style="color: #569cd6;">projects</span><span style="color: #979797;">: </span><span style="color: #cb8f76;">"MyWebApplication.sln"</span></span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">The
file is pretty much self descriptive. As you can see we we have two tasks. The
first task uses nugetrestore passing the solution as input. The second task
executes MSBuild passing the applicatino's solution.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Commit
the file to your local Git Repo and push to commit to TFS.</span></div>
<div style="margin: 0in;">
<br /></div>
<h4 style="margin: 0in; text-align: left;">
Creating the Build definition</h4>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Now
that our yaml file is committed, we will create a build definition to use it.
To do this, click on the New button to create build definition. For the build
template, select YAML and click Apply</span></div>
<div style="margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-jSypoyLPoLU/Wi-TToo1tkI/AAAAAAABr7k/7TQ2tpJLLHQaK4xiqGQrqn8dJgMbAXV0QCLcBGAs/s1600/YAML.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="1600" height="140" src="https://2.bp.blogspot.com/-jSypoyLPoLU/Wi-TToo1tkI/AAAAAAABr7k/7TQ2tpJLLHQaK4xiqGQrqn8dJgMbAXV0QCLcBGAs/s640/YAML.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">We
will then be asked to provide the build name, agent queue and path to Yaml
file. Make sure, you have selected the correct repository and branch in the
"Get Sources" option for the build definition.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Please
note that YAML builds are only supported for Git are not supported when TFVC is
used a version control repository.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Click
on the Triggers tab to make sure that Continuous Integration is selected as
option</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">Click
save to save your build. Now this build is set up as a continuous integration
build for your repository and is triggered with every commit.</span></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<br /></div>
<div style="margin: 0in;">
<span style="font-weight: bold;"><span style="font-family: "verdana" , sans-serif;">Conclusion:</span></span></div>
<div style="margin: 0in;">
<span style="font-family: "verdana" , sans-serif;">If
you compare the amount of work you had to do to create yaml build, it's really
a breeze as compare to TFSBuilds. There are many use cases of using YAML for
your build definitions. You can set up a complete pipeline, decoratively
executing each steps which can be developed and tested locally before being
used in VSTS.</span></div>
<br />
<div style="margin: 0in;">
<br /></div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com2tag:blogger.com,1999:blog-9108944828477346591.post-3354392970215760792017-12-02T02:05:00.001+00:002017-12-02T02:05:52.167+00:00TFS 2017 Build - Partially succeed a build<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: Verdana, sans-serif;">At times, there is a need to explicitly set a Team build's result to be "Partially Successful". </span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">In Xaml, the way to forcibly build to set as partially successful is to set the build's "CompilationStatus" property to true and "TestStatus" to False, as shown below</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><mtbwa:SetBuildProperties DisplayName="Set TestStatus to Failed so we get a PartiallySucceeded build" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" /></span></blockquote>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Setting a TFS 2017 build to partially succeed is a bit more intuitive. Simply add a powershell task with an inline script and set the task's result to "SucceededWithIssues". Make sure it's the last task in your build, so that it doesn't affect the flow of task execution. The Powershell statement is shown below</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">Write-Host "##vso[task.complete result=SucceededWithIssues;]DONE</span><span style="font-family: Verdana, sans-serif;">"</span></blockquote>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">My build looks as follow</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-fh9efM5SGAU/WiIJ2AMiw3I/AAAAAAABr4s/mN7G1PLHS2gduqeLxQ2boR6Cgfo22plaQCLcBGAs/s1600/Partiallysucceed.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="1600" height="168" src="https://3.bp.blogspot.com/-fh9efM5SGAU/WiIJ2AMiw3I/AAAAAAABr4s/mN7G1PLHS2gduqeLxQ2boR6Cgfo22plaQCLcBGAs/s640/Partiallysucceed.PNG" width="640" /></a></div>
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-58540924202384930752017-11-30T01:32:00.000+00:002017-11-30T01:32:07.317+00:00TFS 2017 Build System - Maintain last "N' builds<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "verdana" , sans-serif;">In my <a href="http://hamidshahid.blogspot.com/2017/11/understanding-retention-policies-for-tfs-2017.html" target="_blank">last blog post</a>, I described retention policies in the TFS 2017 build system. I described how different it is from the the retention policies we get in XAML build system. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">One of the limitations I found in the new style retention policy is that I couldn't retain a specific number of builds for each status. We needed to do it for some builds that are triggered very frequently (once every couple of minutes) and check if there is some work to be done. If it found work, it would do it, otherwise it will reschedule a build for itself after a couple of minutes. Another scenario, where you might have a lot of builds is when you it is triggered by a commit of a very busy repository.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">So, in order for us to retain only "N" builds for each status, we created a Powershell Module to clean up builds. In the module, we create a command-let that takes as parameter the name of the build, the number of builds to keep, the result filter and tag filter. Our command-let looks as following</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<div style="background-color: #fcfcfc;">
<blockquote class="tr_bq">
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">***************************************************</span></blockquote>
</blockquote>
<blockquote class="tr_bq">
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">.SYNOPSIS</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Cleans up all builds for the given build definition keeping the latest N number of builds where n is passed a parameter</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> If a status is provided, it would only keep N builds with the given status</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">.DESCRIPTION</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> PATCH https://{instance}/DefaultCollection/{project}/_apis/build/builds/{buildId}?api-version={version}</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Uses api-version 2.0 to update the build result</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">***************************************************</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">function Cleanup-Builds([string] $tfsCollection,</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> [string] $tfsProject,</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> [string] $buildDefinitionName,</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> [int] $numberOfBuildsToKeep = 10,</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> [string] $result="",</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> [string] $tagsFilter = "")</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">{</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (${env:system.debug} -eq $true) {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $VerbosePreference="Continue"</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if ($status -eq ""){</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName."</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> else{</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName with status $status."</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $buildDefinition = Find-BuildDefinition($buildDefinitionName)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if ($buildDefinition -eq $null) {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Error "No build definition found $buildDefinitionName"</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $buildDefinitionId = $buildDefinition.id</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds?api-version=2.0&definitions=$buildDefinitionId&queryOrder=2&resultFilter=$result&tagFilters=$tagsFilter&`$top=5000")</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $builds = Invoke-RestMethod -Method GET -UseDefaultCredentials -ContentType "application/json" -Uri $query</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $retainedBuild = 0</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $deletedBuildCount = 0</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> for ($i = $builds.Count - 1; $i -gt -1; $i--) {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $build = $builds.value[$i]</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $buildId = $build.id</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $buildNumber = $build.buildNumber</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> try {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId/tags?api-version=2.0")</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $tagFound = $false</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> # Not delete the latest numberOfBuildsToKeep builds</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if ( ($retainedBuild -lt $numberOfBuildsToKeep)) {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $retainedBuild = $retainedBuild + 1</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> else {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Verbose "Deleting build $buildNumber"</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId`?api-version=2.0")</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Invoke-RestMethod -Method DELETE -UseDefaultCredentials -ContentType "application/json" -Uri $query</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> $deletedBuildCount = $deletedBuildCount + 1</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> catch {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Error "StatusCode:" + $_.Exception.Response.StatusCode.value__ +</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> "`r`nStatusDescription:" + $_.Exception.Response.StatusDescription</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Write-Output "Deleted $deletedBuildCount builds for build definition $buildDefinitionName"</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">}</span></blockquote>
</blockquote>
</div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">We create a PowerShell Module file for the above command let. To set up the Powershell module, we modified the PSModulePath environment variable as first step of our build to include the module path. Then to set-it all up we added a PowerShell task group calling the <i>Cleanup-Builds </i>command in an inline script as shown below</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-lZazZoNaT0M/Wh9dQJa4stI/AAAAAAABr4Q/QnRpdA38fCMVSyPzkOdwEPG2seFMyB-xgCLcBGAs/s1600/Clean-Up.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="655" data-original-width="1600" height="260" src="https://1.bp.blogspot.com/-lZazZoNaT0M/Wh9dQJa4stI/AAAAAAABr4Q/QnRpdA38fCMVSyPzkOdwEPG2seFMyB-xgCLcBGAs/s640/Clean-Up.jpg" width="640" /></span></a></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br />Our build definition looks like below</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-nxgn4xZYiKE/Wh9eCnyqQ4I/AAAAAAABr4Y/1kGpyKrT-9s3plFvVzjlIW7N88z3qJzFwCLcBGAs/s1600/Cleanup%2Bbuild.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="611" data-original-width="1600" height="243" src="https://3.bp.blogspot.com/-nxgn4xZYiKE/Wh9eCnyqQ4I/AAAAAAABr4Y/1kGpyKrT-9s3plFvVzjlIW7N88z3qJzFwCLcBGAs/s640/Cleanup%2Bbuild.PNG" width="640" /></span></a></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-9865356063943529732017-11-10T00:59:00.004+00:002017-11-20T22:11:08.123+00:00Retention Policies for TFS 2017 Build System<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "verdana" , sans-serif;">TFS build system has had a major overhaul since TFS 2015. For people working on team builds since TFS 2010, there is some major learning curve. One of the things the people often find confusing is the retention policy in the new build system. In earlier versions of TFS, you could specify how many builds you want to retain for each status as shown in the screenshot below</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-AGJvNo6eFAk/WgT3N0Dw1LI/AAAAAAABrx4/ejV0qUIGzJcyx9NMbEUTvqKOzQ3CfD-OgCLcBGAs/s1600/TFS%2Bold%2Bbuild%2Bdefinitions.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="340" data-original-width="1097" height="196" src="https://2.bp.blogspot.com/-AGJvNo6eFAk/WgT3N0Dw1LI/AAAAAAABrx4/ejV0qUIGzJcyx9NMbEUTvqKOzQ3CfD-OgCLcBGAs/s640/TFS%2Bold%2Bbuild%2Bdefinitions.PNG" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b><span style="font-family: "verdana" , sans-serif; font-size: small;">Retention Policies for Xaml Builds</span></b></td></tr>
</tbody></table>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">The retention policy is quite obvious and you have a deterministic number of builds retained at each status. It's not quite the case in the new build system. A sample retention policy in the new system looks like following</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-CknJdai0YVw/WgT4Hqsz_hI/AAAAAAABryA/spEtrM3yZZASVq4e-SrkBJkvUS9T_grFgCLcBGAs/s1600/TFS%2Bnew%2Bbuild%2Bretention.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="569" data-original-width="1600" height="227" src="https://2.bp.blogspot.com/-CknJdai0YVw/WgT4Hqsz_hI/AAAAAAABryA/spEtrM3yZZASVq4e-SrkBJkvUS9T_grFgCLcBGAs/s640/TFS%2Bnew%2Bbuild%2Bretention.PNG" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b><span style="font-family: "verdana" , sans-serif; font-size: small;">Retention Policies for TFS Builds</span></b></td></tr>
</tbody></table>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h4>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">So what does it mean? </span></h4>
<span style="font-family: "verdana" , sans-serif;">Well, to say it simply, it means exactly what it says on the tin!! In the example above, the build would keep all builds from the last 4 days and keep a minimum of 20 builds. That is if there are less than 20 builds present for the given build definition, it would keep older builds until there are a minimum of 20 builds. Lets ignore the options with the lock sign, we will come back to it later. Note there is are no maximum count. It means that you can't control how many builds you keep for your build definition. This is a major shift from earlier retention policy where the number of builds kept for a build definition was deterministic. </span><br />
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h4>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">When are builds actually cleaned up?</span></h4>
<span style="font-family: "verdana" , sans-serif;">If you are using an on-premise version of TFS (I am using TFS 2017 Update 2), the builds are actually not cleaned until 3:00 AM every day. For VSTS, it happens several times in a day but the time is not deterministic. The actually explains why there is only a "Minimum to Keep" option in the retention policy.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">If you have a build definitions that is triggered very frequently, you will need to find a solution of actually deleting the build definitions. I will explain it in the next post.</span><br />
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h4>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">What about Keep For 356 days, 100 good builds?</span></h4>
<span style="font-family: "verdana" , sans-serif;">This is the option you see below your policy in the screenshot shown above. This is in fact a TFS Project Collection wide policy and enforces the maximum retention policy. So, in the example above, you can't set "Days to Keep" to more than 365 and "Minimum to Keep" to more than 100. In fact, if you have appropriate permissions, you can change it for the entire Team Project Collection.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-E2kyW3kaVqE/WgT4prifCAI/AAAAAAABryM/_8XU4H2xUiIOXFEkLEbjIiQRH46--ISOQCLcBGAs/s1600/TFS%2Bbuild%2Bpolicies.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="446" data-original-width="414" height="400" src="https://3.bp.blogspot.com/-E2kyW3kaVqE/WgT4prifCAI/AAAAAAABryM/_8XU4H2xUiIOXFEkLEbjIiQRH46--ISOQCLcBGAs/s400/TFS%2Bbuild%2Bpolicies.PNG" width="371" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b><span style="font-family: "verdana" , sans-serif; font-size: small;">TFS Project Collection Retention Policy</span></b></td></tr>
</tbody></table>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h4>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">Multiple Policies</span></h4>
<span style="font-family: "verdana" , sans-serif;">If you want, you can add retention multiple policies for your build definition. It is very useful, if you have build definition that builds different code branches (release branches for instance). You can use the retention policies to keep different number of builds from each branch. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-pnTeuKCx6AA/WgT47EUKJmI/AAAAAAABryQ/GgB2QIZ-77IFCjO69t9Kemub8MkMeR-NACLcBGAs/s1600/Multiple%2BPolicies.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-family: "verdana" , sans-serif;"><img border="0" data-original-height="568" data-original-width="1342" height="270" src="https://2.bp.blogspot.com/-pnTeuKCx6AA/WgT47EUKJmI/AAAAAAABryQ/GgB2QIZ-77IFCjO69t9Kemub8MkMeR-NACLcBGAs/s640/Multiple%2BPolicies.PNG" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b><span style="font-family: "verdana" , sans-serif; font-size: small;">Multiple Policies</span></b></td></tr>
</tbody></table>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">If you have multiple retention policies for the same branch, the retention would be the most lenient of all the retention, so whatever retains the most builds.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">In the next blog post, I will show how we are keeping a lid on the number of builds for builds which are build very frequently, every couple of minutes in our case.</span></div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com1tag:blogger.com,1999:blog-9108944828477346591.post-35370953634177314492017-10-22T23:32:00.001+01:002017-10-22T23:32:29.891+01:00C# Async Programming - Tasks for dummies<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
There are umpteenth articles / blogs / guides about
the <a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap">Task-based
Asynchronous Pattern</a> used in C# for
asynchronous programming. However, I feel that explanations are often
convoluted and difficult to follow for something new to language / programming.
This week, I explained the pattern to a graduate following some review comments
and couldn't find an easy-to-understand article, so thought to explain it
myself here.</div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
This
will be a series of blogs. I will try to keep it as simple as possible without
compromising on completeness. Starting with one of the most basic concept -
"The Task".</div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-weight: bold;">What is a Task?</span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Task is
the C# abstraction of an asynchronous operation. In other words, it some series
of code that executes asynchronously. It may or may/not return a result. </div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-weight: bold;">How are Tasks created?</span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Tasks
can be created explicitly by creating an object of type Task or Task or
implicitly by running an async method. For example, both of these expressions
end up resulting a task</div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in 0in 0in 0.375in;">
<br /></div>
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">var task = Task.Run( () => { … });</span></code><br />
<br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">Func<task> taskFunction = ( async () => { await foo() }
); taskFunction.Invoke();</task></span></code><br />
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-weight: bold;">Tasks and Threads</span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
There
are some differences when tasks are created explicitly or implicitly but let's
not go there. </div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
There is
a common misunderstanding that creating a task means running on a new thread.
This is not true. Whether or not task creates a new thread depends upon how it
is created.</div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
For
tasks created using Task Parallel Library, using Task.Run() for instance, a
thread is created with the task. Running the following code </div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<code style="font-family: Consolas; font-size: 11.0pt; margin: 0in;"> </code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">Console.WriteLine($"Application Thread ID :
{Thread.CurrentThread.ManagedThreadId}");</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">Task.Run(() =></span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">{</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .75in; margin: 0in;"><span style="color: #cc0000;">Thread.Sleep(30);</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .75in; margin: 0in;"><span style="color: #cc0000;">Console.WriteLine("Inside Task");</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .75in; margin: 0in;"><span style="color: #cc0000;">Console.WriteLine($"Task Thread ID :
{Thread.CurrentThread.ManagedThreadId}");</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin-left: .375in; margin: 0in;"><span style="color: #cc0000;">});</span></code><br />
<br />
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Will
produce</div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<code style="background-color: white; font-family: Consolas; font-size: 9pt; margin: 0in;"><span style="color: #38761d;">Application
Thread ID : 2<br />
Back to application Thread ID : 2<br />
Inside Task</span></code><br />
<code style="background-color: white; font-family: Consolas; font-size: 9pt; margin: 0in;"><span style="color: #38761d;">Task Thread ID
: 3</span></code><br />
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Tasks
created by async methods <span style="text-decoration-line: underline;">DO NOT</span> create
a new thread. Once once task is blocked, control is shifted to other task that
is in ready state. </div>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
For
example, the following code </div>
<br />
<blockquote class="tr_bq">
<span style="color: #cc0000;"><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">Console.WriteLine($"Application
Thread ID :
{Thread.CurrentThread.ManagedThreadId}");</code></span></blockquote>
<blockquote class="tr_bq">
<span style="color: #cc0000;"><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">Func<task> localTask = (async () =></task></code><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">{</code><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">Console.WriteLine("Inside Task");</code><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">Thread.Sleep(30);</code></span> </blockquote>
<blockquote class="tr_bq">
<span style="color: #cc0000; font-family: Consolas; font-size: 9pt;">Console.WriteLine($"Task Thread ID :{Thread.CurrentThread.ManagedThreadId}");</span></blockquote>
<blockquote class="tr_bq">
<span style="color: #cc0000;"><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">});</code></span></blockquote>
<blockquote class="tr_bq">
<span style="color: #cc0000;"><code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;">localTask.Invoke(); </code></span></blockquote>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
<span style="font-size: 11pt;"> </span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Will
produce </div>
<br />
<code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;"><span style="color: #38761d;">Application
Thread ID : 2<br />
Inside Task</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;"><span style="color: #38761d;">Back to
application Thread ID : 2</span></code><br />
<code style="font-family: Consolas; font-size: 9.0pt; margin: 0in;"><span style="color: #38761d;">Task Thread ID
: 2</span></code><br />
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
Note
that the thread Id is the same. This means that tasks, unless created by
task parallel library, do not run in parallel. They share the same thread and
uses context switching to pass control as tasks are blocked and become
available again.</div>
<div style="font-family: Calibri; font-size: 12pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 12pt; margin: 0in;">
<span style="font-weight: bold;">The Await Operator</span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
This
brings us nicely to the await operator. In simplest words, <span style="font-weight: bold;">the await operator cause context switching</span>. The
operator is used when executing code block needs to get a result from a task
that is running asynchronously. Calling await will block the current routine,
only to be returned when the task it is waiting on has completed.</div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-weight: bold;">Conclusion</span></div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
I hope
this post will help people in understanding C# tasks. Some of the key take away
from this post</div>
<ul style="direction: ltr; margin-bottom: 0in; margin-left: .375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Calibri; font-size: 11.0pt;">Tasks can be explicitly using
Task Parallel Library or implicitly using async keyword.</span></li>
<li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Calibri; font-size: 11.0pt;">Task are not same as threads.
Some tasks are created in a new thread - the ones created by TPL for
instance. While others are created on the same thread.</span></li>
</ul>
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
</div>
<div style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div>
<div style="orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-indent: 0px; widows: 2;">
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: normal; letter-spacing: normal; text-transform: none; white-space: normal; word-spacing: 0px;">
</div>
</div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-81486315210044495722017-10-15T20:23:00.001+01:002017-10-21T20:32:52.591+01:00Visual Studio 2017 - New npm package won't install...<div dir="ltr" style="text-align: left;" trbidi="on">
One of my "how I got burned today" blogs. Spent some time on it so thought to share. <br />
<br />
I started with writing a simplistic application using NodeJS today using Visual studio 2017. Tried to install NodeJS using the "Install new npm Package option".<br />
<br />
<a href="https://1.bp.blogspot.com/-fWFTR9QM_O8/WeO0VmtMFXI/AAAAAAABrs4/CP5463n58KMp5BJumwvwTlT0Atx9rrIAQCLcBGAs/s1600/2017-10-15_12-17-11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="282" data-original-width="608" height="147" src="https://1.bp.blogspot.com/-fWFTR9QM_O8/WeO0VmtMFXI/AAAAAAABrs4/CP5463n58KMp5BJumwvwTlT0Atx9rrIAQCLcBGAs/s320/2017-10-15_12-17-11.png" width="320" /></a><br />
<br />
The "Install New npm package" dialog opens. Typed in the name of the package and clicked install "Install Package"<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://3.bp.blogspot.com/-LzapguPQ5JM/WeO030ZeGlI/AAAAAAABrs8/KrddwcXush8wMbdIKoPkSHbKBoO3qNcswCLcBGAs/s1600/2017-10-15_12-19-41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1018" data-original-width="1364" height="238" src="https://3.bp.blogspot.com/-LzapguPQ5JM/WeO030ZeGlI/AAAAAAABrs8/KrddwcXush8wMbdIKoPkSHbKBoO3qNcswCLcBGAs/s320/2017-10-15_12-19-41.png" width="320" /></a></div>
<br />
Absolutely nothing happened. No errors or messages were shown and my package wasn't installed. <b>Turned out that there was a syntax error in my packages.json file, where I had missed out a comma</b>. Would have been nice if visual studio had captured this error and shown some sort of message. I am using Visual Studio 2017 Update 3.</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-74555422285418866732017-10-13T07:11:00.001+01:002017-10-13T07:14:04.670+01:00PowerShell - The curious case of @ in converted Json strings<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;">PowerShell is great when it comes to working with JSON. Being a scripting language, you can pretty
much de-serialize your json without declaring types for them, do your work on de-serialized objects, and then serialize it back to for storing or transport. </span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;"><br /></span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;">The <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-json?view=powershell-5.1" target="_blank">ConvertFrom-Json</a> and <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json?view=powershell-5.1" target="_blank">ConvertTo-Json</a> are
powerful functions. <span style="font-size: 11pt;">However, there are a
few nuggets that you need to be aware of. I got caught out by one of the so
thought to blog about it.</span></span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;"><br /></span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;">When working with
ConvertTo-Json, be mindful of the <i>-Depth </i>parameter. The parameter specifies how
deep it should go in our object while converting to json string. The default
value is 2. What it <span style="font-size: 11pt;">means is that if you have a complex json object that goes down more than two levels of depth and it you haven't specified
the -Depth parameter, your nested objects would be treated as
Hashtable.</span></span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;"><br /></span></div>
<div style="margin: 0in;">
<div style="font-size: 11pt;">
<span style="font-family: inherit;">As an example, let's
assign a json string to a variable<span style="font-size: 11pt;"> </span></span></div>
<div style="font-family: calibri; font-size: 11pt;">
<span style="font-size: 11pt;"><br /></span></div>
<div>
<pre style="background: #f7f7f7; border: 1px dashed rgb(204, 204, 204);"><span style="font-family: "courier new" , "courier" , monospace;"><code style="word-wrap: normal;"><span style="color: black;"> </span></code><span style="font-family: "courier new" , "courier" , monospace;">$programmersJson = '[{
"Name" : "Hamid",
"Gender" : "Male",
"Expertise": [
{
"Skill": "PowerShell",
"Level": "5"
},
{
"Skill": "C#",
"Level": "8"
}
]},
{
"Name" : "Adnan",
"Gender" : "Male" ,
"Expertise": [
{
"Skill": "PowerShell",
"Level": "7"
},
{
"Skill": "C#",
"Level": "6"
}
]}]'</span></span></pre>
</div>
<div style="font-family: calibri; font-size: 11pt;">
<span style="font-size: 11pt;"><br /></span></div>
</div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;">The json string contains an object that is three level deep. . The top level object is a
collection, each item in the collection is an object with a "Name" and
"Gender" property as well as a collection of objects each has a "Skill" and "Level" property</span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;"><br /></span></div>
<div style="font-size: 11pt; margin: 0in;">
<span style="font-family: inherit;">Now, lets call ConvertFrom-Json</span><br />
<br /></div>
<pre style="background-color: #f7f7f7; border: 1px dashed;"><span style="font-family: "courier new" , "courier" , monospace;">$programmers = ConvertFrom-Json -InputObject $programmersJson
Write-Output $programmers</span></pre>
<div style="margin: 0in;">
<div style="font-size: 11pt;">
<br /></div>
<div style="font-size: 11pt;">
<span style="font-family: inherit; font-size: 14.6667px;">The result is an array of objects, as expected</span></div>
<div style="font-size: 11pt;">
<span style="font-family: "calibri"; font-size: 14.6667px;"><br /></span></div>
<pre style="background-color: #f7f7f7; border: 1px dashed;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: 14.6667px;">Name Gender Expertise
---- ------ ---------
Hamid Male {@{Skill=PowerShell; Level=5}, @{Skill=C#; Level=8}}
Adnan Male {@{Skill=PowerShell; Level=7}, @{Skill=C#; Level=6}}</span></span></pre>
<div style="font-size: 11pt;">
<span style="font-family: "calibri"; font-size: 14.6667px;"></span></div>
<div style="margin: 0in;">
<div style="font-size: 14.6667px;">
<span style="font-family: "calibri"; font-size: 14.6667px;"><br /></span></div>
<div style="font-size: 14.6667px;">
<span style="font-size: 14.6667px;"><span style="font-family: inherit;">Now, lets try to convert it back to json. So, when you call</span></span></div>
<div style="font-size: 14.6667px;">
<span style="font-size: 14.6667px;"><br /></span></div>
<pre style="background-color: #f7f7f7; border: 1px dashed;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: 14.6667px;"> ConvertTo-Json -InputObject $programmers</span></span></pre>
<div style="font-size: 14.6667px;">
</div>
<div style="margin: 0in;">
<div style="font-size: 14.6667px;">
<span style="font-family: "calibri"; font-size: 14.6667px;"><br /></span></div>
<div style="font-size: 14.6667px;">
<span style="font-size: 14.6667px;"><span style="font-family: inherit;">You would expect the Json string to be same as $programmersJson. Wrong!! The string you get back is</span></span></div>
<div style="font-size: 14.6667px;">
<br /></div>
<div style="font-size: 14.6667px;">
</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: calibri; font-size: 11pt; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: normal; letter-spacing: normal; margin: 0in; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">
</div>
<br />
<div style="margin: 0in; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-indent: 0px; widows: 2;">
<div>
<pre style="background: rgb(247, 247, 247); border: 1px dashed rgb(204, 204, 204); margin: 0px;"><span style="font-family: "courier new" , "courier" , monospace;">[
{
"Name": "Hamid",
"Gender": "Male",
"Expertise": [
"@{Skill=PowerShell; Level=5}",
"@{Skill=C#; Level=8}"
]
},
{
"Name": "Adnan",
"Gender": "Male",
"Expertise": [
"@{Skill=PowerShell; Level=7}",
"@{Skill=C#; Level=6}"
]
}
]</span></pre>
</div>
</div>
<div style="font-size: 14.6667px;">
<br /></div>
<div style="font-size: 14.6667px;">
<span style="font-family: inherit;">Notice, the @ sign for each of the item in the Expertise collection. It means that the function has treated each item as a Hashtable rather than an object.</span></div>
</div>
</div>
</div>
<div style="margin: 0in;">
<div style="margin: 0in;">
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Now execute the following</span><br />
<br />
<pre style="background-color: #f7f7f7; border: 1px dashed;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: 14.6667px;"> ConvertTo-Json -InputObject $programmers -Depth 3</span></span></pre>
<div style="margin: 0in;">
<div style="font-size: 14.6667px;">
<br /></div>
</div>
<div style="margin: 0in;">
<span style="font-family: inherit; font-size: 14.6667px;">The depth parameter will make it treat each item of Expertise collection as an object as well and the resulting Json would be as you would expect.</span><br />
<br />
<div style="margin: 0in;">
<pre style="background: rgb(247, 247, 247); border: 1px dashed rgb(204, 204, 204);"><span style="font-family: "courier new" , "courier" , monospace;">[
{
"Name": "Hamid",
"Gender": "Male",
"Expertise": [
{
"Skill": "PowerShell",
"Level": "5"
},
{
"Skill": "C#",
"Level": "8"
}
]
},
{
"Name": "Adnan",
"Gender": "Male",
"Expertise": [
{
"Skill": "PowerShell",
"Level": "7"
},
{
"Skill": "C#",
"Level": "6"
}
]
}
]</span></pre>
</div>
<span style="font-size: 14.6667px;"></span><br />
<div style="font-size: 14.6667px;">
<span style="font-family: inherit; font-size: 14.6667px;"><br /></span></div>
<div style="font-size: 14.6667px;">
<span style="font-family: inherit; font-size: 14.6667px;">This is how we would have expected the output json string to look like. So, next time you are working with json on PowerShell, make sure to be mindful of the -Depth parameter.</span></div>
<span style="font-family: inherit; font-size: 14.6667px;"><br /></span></div>
</div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-60674319547572775192017-10-09T07:15:00.002+01:002017-10-23T04:51:03.509+01:00Migrating ASP.NET MVC website to ASP .NET Core<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;">I maintain an ASP.NET MVC website that I have been meaning to move to ASP.NET Core, but found the .Net Core 1.1 library rather limited. With the release of <a href="https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/" target="_blank">.NET Core 2.0 and ASP.NET Core 2.0</a>, we decided to migrate the websites to the new framework. The site has been operational since October 2010 and was built using ASP.NET MVC 2.0. It has gone through various bouts of upgrades and is currently using ASP.NET MVC 5.2.0, which forms the baseline of this conversion. I had several discoveries along the way so thought to blog about them. </span></div>
<br />
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;">In this post, I am going to write about prep and moving our "Model" to </span><a href="https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-entity-framework-core-2-0/" style="font-family: verdana, sans-serif;" target="_blank">Entity Framework .Core 2.0</a><span style="font-family: "verdana" , sans-serif;">. </span></div>
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;"><b>Background</b></span><br />
<span style="font-family: "verdana" , sans-serif;"><b><br /></b></span></div>
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;">The model of our website was built using Entity Framework code first. All database operations were performed using repository pattern. Our repository interface looks as follows</span></div>
<br />
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black; font-family: "courier";">
<blockquote class="tr_bq">
public interface IRepository<tentity> : IDisposable where TEntity : class</tentity></blockquote>
<blockquote class="tr_bq">
{</blockquote>
<blockquote class="tr_bq">
IQueryable<tentity> GetQuery();</tentity></blockquote>
<blockquote class="tr_bq">
IEnumerable<tentity> GetAll();</tentity></blockquote>
<blockquote class="tr_bq">
IEnumerable<tentity> Find(Expression<func bool="" ntity="">> predicate);</func></tentity></blockquote>
<blockquote class="tr_bq">
TEntity Single(Expression<func bool="" ntity="">> predicate);</func></blockquote>
<blockquote class="tr_bq">
TEntity First(Expression<func bool="" ntity="">> predicate);</func></blockquote>
<blockquote class="tr_bq">
void Add(TEntity entity);</blockquote>
<blockquote class="tr_bq">
void Delete(TEntity entity);</blockquote>
<blockquote class="tr_bq">
void Attach(TEntity entity);</blockquote>
<blockquote class="tr_bq">
void SaveChanges();</blockquote>
<blockquote class="tr_bq">
DbContext DataContext { get; }</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
<br />
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;">We use interface inheritance to create repository for each of our model objects, so for a object "Token", the repository looks like following</span></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black; font-family: "courier";">
<blockquote class="tr_bq">
public interface ITokenRepository : IRepository<token></token></blockquote>
<blockquote class="tr_bq">
{</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
<br />
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;">With the interface inheritance in place. our single generic repository class can the logic for database operations as shown below</span></div>
<div style="text-align: justify;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black;">
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">public class Repository<tentity> : IRepository<tentity> where TEntity : class</tentity></tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> private DbContext _context;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> private IDbSet<tentity> _dbSet;</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> private static string _connectionString = string.Empty;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public Repository(IDataContextFactory dbContextFactory)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (string.IsNullOrWhiteSpace(dbContextFactory.ConnectionString))</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context = dbContextFactory.Create(ConnectionString);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> else</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context = dbContextFactory.Create();</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _dbSet = _context.Set<tentity>();</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public Repository(DbContext context)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context = context;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _dbSet = _context.Set<tentity>();</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public DbContext DataContext</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> get</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return _context;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public IQueryable<tentity> GetQuery()</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return _dbSet;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public IEnumerable<tentity> GetAll()</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return GetQuery().AsEnumerable();</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public IEnumerable<tentity> Find(Expression<func bool="" ntity="">> predicate)</func></tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return _dbSet.Where<tentity>(predicate);</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public TEntity Single(Expression<func bool="" ntity="">> predicate)</func></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return _dbSet.SingleOrDefault<tentity>(predicate);</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public TEntity First(Expression<func bool="" ntity="">> predicate)</func></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> return _dbSet.FirstOrDefault<tentity>(predicate);</tentity></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public void Delete(TEntity entity)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (entity == null)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> throw new ArgumentNullException("entity");</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _dbSet.Remove(entity);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public void Add(TEntity entity)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (entity == null)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> throw new ArgumentNullException("entity");</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _dbSet.Add(entity);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public void Attach(TEntity entity)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _dbSet.Attach(entity);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public void SaveChanges()</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context.SaveChanges();</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public void Dispose()</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> Dispose(true);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> GC.SuppressFinalize(this);</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> protected virtual void Dispose(bool disposing)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (disposing)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (_context != null)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context.Dispose();</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _context = null;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public static string ConnectionString </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> get</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> if (string.IsNullOrWhiteSpace(_connectionString))</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> _connectionString = ConfigurationManager.ConnectionStrings["Rewards"].ConnectionString;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier";"> return _connectionString;</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier";"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier";"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier";"> }</span></blockquote>
</blockquote>
<br />
<span style="font-family: "verdana" , sans-serif;">The class above does all the heavy lifting for us. We just need to define classes that implement each of our models' repository interface. For our model Token, it would be</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<br />
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black;">
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">public class TokenRepository : Repository<token> , ITokenRepository</token></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public TokenRepository(IDataContextFactory dbContextFactory)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> : base(dbContextFactory)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> { </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> public TokenRepository(DbContext dataContext) </span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> : base(dataContext)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> }</span></blockquote>
<div style="font-family: Verdana, sans-serif;">
<br /></div>
</blockquote>
<br />
<span style="font-family: "verdana" , sans-serif;"><b>Entity Framework Core 2.0 limitations</b></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><b>1. No Many-To-Many Relationship</b></span><br />
<span style="font-family: "verdana" , sans-serif;"><b><br /></b></span>
<span style="font-family: "verdana" , sans-serif;">The biggest issue we have encountered while migrating to .Net Core 2.0 is lack of resolution for Many-To-Many relationships. This is an <a href="https://github.com/aspnet/EntityFrameworkCore/issues/1368" target="_blank">open issue</a>, which haven't been resolved yet. For us, it means a lot of re-work.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">With the POCO way of working, you would start with writing your domain model and your write your business logic using models, without really thinking about relational database details. We have a lot of code where our LINQ queries were based on domain model relationships. Now, we need to re-work all those. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">This, in my mind, is a major issue and though ways to resolve this issue, it prevents Entity Framework .Core from being a true ORM tool. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">As an example, consider you have two entities Parent and Student in your model, where a student can have multiple parents and a parent can have multiple students. With Entity Framework 6, the model definition was sufficient to imply the correct type of relationship. If you have to do it explicitly, you could do it at the time of model creation like below</span><br />
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black;">
<blockquote class="tr_bq" style="background-color: #f9f9f9; color: black;">
<span style="font-family: "courier new" , "courier" , monospace;">modelBuilder.Entity<student>()</student></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> .HasMany<parent>(p => p.Parents)</parent></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> .WithMany(r => r.Students)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> .Map(m =></span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> {</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> m.ToTable("ParentStudents");</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> m.MapLeftKey("Student_ID");</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> m.MapRightKey("Parent_ID");</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;"> });</span></blockquote>
</blockquote>
<span style="font-family: "verdana" , sans-serif;">You can then go on and work with defining a collection of Parents in Students class and a collection of Students in Parent class. The <a href="https://msdn.microsoft.com/en-us/library/gg696499(v=vs.113).aspx" target="_blank">.WithMany()</a> method is not there in Entity Framework Core.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">The lack of Many-To-Many feature in EF Core is hard to justify. POCO came out as a good model for domain driven development and not supporting many-to-many in a domain driven world is hard to justify. We didn't want to "dilute" the model with resolving entities, so we decided to "implement" the many-to-many resolution in our code. This <a href="https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-4-a-more-general-abstraction/" target="_blank">series of post</a> describes a good way of keeping domain relationship in our objects, so that there is no change in business logic in other parts of the application.</span><br />
<br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><b>2. IDbSet Interface </b></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">The <a href="https://msdn.microsoft.com/en-us/library/gg679233(v=vs.113).aspx" target="_blank">IDbSet</a> interface was removed in Entity Framework 6.0, because the team were looking to add new operations to it without defining a new set of interfaces. This is pretty well documented in <a href="https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20May%2016%2c%202013" target="_blank">EF 6.0 design decisions</a>. I do not agree to this decision as it breaks the whole promise of interface as immutable being. The EF team wanted to avoid creating interfaces like IDBSet2, etc for more functions they decided to do away with it. However, the interface is still present in the in EntifyFramework 6.0 library, so our code still worked. Now we had to replace any use of IDbSet with the <a href="https://msdn.microsoft.com/en-us/library/gg696460(v=vs.113).aspx" target="_blank">DBSet</a> class. Also, meant our test code had to be re-written as we mocked IDbSet to for results from database.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><b>3. No Lazy Loading</b></span><br />
<span style="font-family: "verdana" , sans-serif;"><b><br /></b></span>
<span style="font-family: "verdana" , sans-serif;">The entity framework does not support lazy loading as of yet. There is an <a href="https://github.com/aspnet/EntityFrameworkCore/issues/3797" target="_blank">open issue</a> for it on github. The feature request is in the backlog of EF team but there is no date of adding it yet. Lazy loading is the default behaviour of Entity Framework and would be there for you if you have the navigation property defined as virtual. This is another big way in which Entity Framework core breaks backward compatibility. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">The way around is to "Eager Loading" i.e. ensure that you use the .Include("") and .ThenInclude("") method in all places, where you are relying on Lazy loading. This is no simple as it's easy to miss it out at placed and the error is only manifested at run time. One way of go about doing it, is to find references of all virtual properties and add .Include("") where the object is "hydrated".</span><br />
<br />
<br />
<b style="font-family: verdana, sans-serif;">4. No GroupBy Translation</b><br />
<b style="font-family: verdana, sans-serif;"><br /></b>
<span style="font-family: "verdana" , sans-serif;">Entity Framework Core 2.0 doesn't support translate Group By to SQL. So, if your application is using GroupBy(<predicate>) method, you might need to take a look for alternatives. Fortunately, <a href="https://github.com/aspnet/EntityFrameworkCore/issues/2341" target="_blank">more support </a>for Group By is getting added in EF Core 2.1.</predicate></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">The only way to resolve this issue without punitive performance impact is to move the logic to stored procedures. We were using GroupBy mostly in our reports, which were already a candidate to use stored procedures. So, although there was some work involved but the result was much better performance.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><b>Final Words...</b></span><br />
<span style="font-family: "verdana" , sans-serif;"><b><br /></b></span>
<span style="font-family: "verdana" , sans-serif;">My experiences with migrating code from Entity Framework 6.0 to Entity Framework Core 2.0 would not have uncovered all pertaining issues in migration process but this post might help out someone who is looking to take the plunge. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">In my view, Entity Framework Core 2.0 is still a bit under cooked but if you are willing to take do the extra effort, it has enough functionality for you to move your model / data libraries to it.</span></div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com5tag:blogger.com,1999:blog-9108944828477346591.post-29991804883695461192017-09-23T08:11:00.000+01:002017-09-23T08:11:26.180+01:00Extending Team Explorer in Visual Studio 2017<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="-en-clipboard: true;">
<span style="font-family: inherit;">Visual studio extensibility has always been a great feature in Visual Studio and enhance the entire development experience. With Visual Studio 2017, there were a bunch of very substantial changes made with respect to extensibility. Most of these changes comes from the fact that Visual Studio now supports a lighter installation version with bare minimum feature installation as default. There is also the option to have multiple installation on the same machine. So, what does it mean for for extensions? </span></div>
<div>
<br /></div>
<div>
<span style="font-family: inherit;">VS2017 extensions now following the vsix v3 file format. If you have an extension for earlier visual studio versions and you want to port it to VS2017, it means a whole bunch of changes. Here, I am going to write an extension that demonstrate extending Team Explorer. We will create a very simple extension that has a button on Team Explorer, which will open notepad.</span></div>
<div>
<br /></div>
<div>
<div style="text-align: left;">
<span style="font-weight: bold;">Project Creation & Dependencies</span></div>
<div>
<br />
Let's start with creating a new extensibility vsix project. You will only see the option if you had selected the VS SDK option while installing visual studio. Let's call our project TeamExplorerExtSample. Visual Studio 2017 uses .Net Framework 4.6.1, so we select this version.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://3.bp.blogspot.com/-2YFWx19fuCo/WcYG1KE-4QI/AAAAAAABrjo/lLYleiWK9_oIQAFUH_v1-5qZ1brqfQMUQCLcBGAs/s1600/createproject.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="745" data-original-width="1317" height="361" src="https://3.bp.blogspot.com/-2YFWx19fuCo/WcYG1KE-4QI/AAAAAAABrjo/lLYleiWK9_oIQAFUH_v1-5qZ1brqfQMUQCLcBGAs/s640/createproject.png" width="640" /></a></div>
<div>
<br /></div>
<div>
<br />
Once the project is created, you will see a couple of web files and a file called source.extension.vsixmanifest, which contains extension information. We will come to this file later.<br />
<div>
<br /></div>
<div>
<span style="line-height: 1.45;">Now let's add references to the assemblies we would need to extend Team Explorer. Note that with visual studio 2017, assemblies are not added to GAC so we would need to make sure that all desired assemblies are included in the vsix. </span><span style="line-height: 1.45;">To display a navigation button in team explorer, we would need to implement the interface ITeamExplorerNavigationItem2, so we would need to add references to the following assemblies</span></div>
<div>
<ul style="text-align: left;">
<li> Microsoft.TeamFoundation.Controls</li>
<li> System</li>
<li> System.ComponentModel.Composition</li>
<li> System.Drawing</li>
</ul>
<div>
<div>
<span style="font-weight: bold;"><br /></span></div>
<div>
<span style="font-weight: bold;">VSIX Manifest file:</span></div>
<div>
<br /></div>
<div>
The manifest file contains information about the extension, it's dependencies, assets and pre-requisites. Double click on the source.extension.vsixmanifest to see details. To extend Team Explorer, the key thing to remember is to add the assembly containing classes that implement Team Explorer interfaces as a MEF component. This will ensure that visual studio loads it up when loading team explorer.</div>
<div>
<br /></div>
<div>
Our VSIX manifest file looks like this</div>
</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://2.bp.blogspot.com/-Zr-5dYsb1go/WcYHk7uKB-I/AAAAAAABrjw/VGrD9z_FMYoLJTN_5leCPHlesarkaxB5QCLcBGAs/s1600/vsixmanifest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="707" data-original-width="1600" height="282" src="https://2.bp.blogspot.com/-Zr-5dYsb1go/WcYHk7uKB-I/AAAAAAABrjw/VGrD9z_FMYoLJTN_5leCPHlesarkaxB5QCLcBGAs/s640/vsixmanifest.png" width="640" /></a></div>
<div>
<br /></div>
<div>
<div>
<span style="font-weight: bold;">Extending ITeamNavigationItem2</span></div>
<div>
<br /></div>
<div>
Our extension will create a button in Team Explorer that opens up the notepad application. To do this, we need to extend the ITeamNavigationItem2 interface. The interface is found in Microsoft.TeamFoundation.Control assembly that we have already referenced. We will also need to add TeamExplorerNavigationItem attribute. Our very simple class looks as below.</div>
</div>
<div>
<br /></div>
<div>
<div>
<br /></div>
<div style="background-color: #fbfaf8; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.14902); box-sizing: border-box; font-size: 12px; padding: 8px;">
<div>
<span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">namespace</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">TeamExplorerExtSample</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">{</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">using</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">System;</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">using</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">System.ComponentModel;</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">using</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">System.Diagnostics;</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">using</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">System.Drawing;</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">using</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Microsoft.TeamFoundation.Controls;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">TeamExplorerNavigationItem</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">(</span><span style="color: #a31515; font-family: Consolas; font-size: 9pt; min-height: 10pt;">"C9B2CF74-0C87-4CEA-ACA9-8CC1C816D7F3"</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">, 1800)]</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">class</span> <span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">NotepadNavigationItem</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">:</span> <span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">ITeamExplorerNavigationItem2</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> {</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">bool</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">IsEnabled =></span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">true</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">int</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">ArgbColor => 0;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">object</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Icon =></span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">null</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">string</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Text =></span> <span style="color: #a31515; font-family: Consolas; font-size: 9pt; min-height: 10pt;">"Open Notepad"</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Image</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Image =></span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">null</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">bool</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">IsVisible =></span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">true</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">event</span> <span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">PropertyChangedEventHandler</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">PropertyChanged;</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">void</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Dispose()</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> {</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">this</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">.Dispose(</span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">true</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">);</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">GC</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">.SuppressFinalize(</span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">this</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">);</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> }</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">protected</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">virtual</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">void</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Dispose(</span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">bool</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">disposing)</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> {</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> }</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">void</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Execute()</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> {</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Process</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">.Start(</span><span style="color: #a31515; font-family: Consolas; font-size: 9pt; min-height: 10pt;">"notepad.exe"</span><span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">);</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> }</span></div>
<div style="min-height: 10pt;">
</div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">public</span> <span style="color: blue; font-family: Consolas; font-size: 9pt; min-height: 10pt;">void</span> <span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">Invalidate()</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> {</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> }</span></div>
<div style="min-height: 10pt;">
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;"> }</span></div>
<div>
<span style="color: #333333; font-family: Consolas; font-size: 9pt; min-height: 10pt;">}</span></div>
</div>
<div>
<br /></div>
</div>
<div>
<span style="line-height: 1.45;">As you can see, the only matter we have got in the class is a call to Process.Start to start up notepad. The navigation item appears as below</span>
</div>
<div>
<span style="line-height: 1.45;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://4.bp.blogspot.com/-_Pyy883rAK8/WcYIACRiPlI/AAAAAAABrj0/cEXBR5HiyFgjgWMDtQqdFpCCC43zh-VtwCLcBGAs/s1600/teamexplorer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="497" data-original-width="822" height="241" src="https://4.bp.blogspot.com/-_Pyy883rAK8/WcYIACRiPlI/AAAAAAABrj0/cEXBR5HiyFgjgWMDtQqdFpCCC43zh-VtwCLcBGAs/s400/teamexplorer.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Click on the button and a new instance of notepad opens up.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div>
<span style="font-weight: bold;">Conclusion:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Admittedly. this is a very simplistic extension but contains all the steps you need to extend Team Explorer. You can add classes to add Pages, Sections and Links in Team Explorer, add icons \ images and menu items. The code sample from post is <a href="https://github.com/hamidshahid/teamexplorerextsample">here</a>. </div>
<div>
<span style="line-height: 1.45;"><br /></span></div>
</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com10tag:blogger.com,1999:blog-9108944828477346591.post-54439509765194730802017-09-22T02:25:00.001+01:002017-09-22T02:25:12.519+01:00Shelveset Comparer now supports Visual Studio 2017<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; font-size: 13.2px;"> The popular Shelveset comparer extension that I created a few years ago now support Visual Studio 2017 as well. </span></span><div>
<span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; font-size: 13.2px;"><br /></span></span></div>
<div>
<span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; font-size: 13.2px;">It took me some time to create a compatible version due to load of things happening in personal life. </span></span><span style="background-color: white; color: #666666; font-family: Arial, Helvetica, sans-serif; font-size: 13.2px;">There is also the added reason that I am not using Git for almost all projects I am working on, so the need for shelveset & comparisons wasn't felt as much as it would have. </span><div>
<br /></div>
<div>
<span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white;"><span style="font-size: 13.2px;">While working on creating the new version, I had to learn about very substantial changes in visual studio extensibility. I will write a blog about it. Please feel free to download the extension & give me your feedback</span></span></span></div>
</div>
<div>
<br /></div>
<div>
<span style="background-color: white; font-size: 13.2px;"><span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><a href="https://marketplace.visualstudio.com/items?itemName=HamidShahid.ShelvesetComparer-19329">https://marketplace.visualstudio.com/items?itemName=HamidShahid.ShelvesetComparer-19329</a></span></span></div>
<div>
<br /></div>
<div>
<span style="background-color: white; color: #666666; font-family: Arial, Helvetica, sans-serif; font-size: 13.2px;"><ciao /></span></div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-38406183788157080352016-12-19T17:22:00.000+00:002016-12-19T17:22:01.594+00:00ALM Rangers blog - Sending Email Notification from VSTS/TFS Build<div dir="ltr" style="text-align: left;" trbidi="on">
Please read my post on the Microsoft ALM Rangers blog regarding sending notification emails from Build vNext.<br />
<h3 style="text-align: left;">
<a href="https://blogs.msdn.microsoft.com/visualstudioalmrangers/2016/12/19/sending-email-notification-from-vststfs-build/" target="_blank">Blog Post</a> </h3>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-51002151431634528542016-10-27T13:36:00.000+01:002018-06-19T17:32:24.934+01:00Running a VSTS Build Agent on a Windows Container<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "verdana" , sans-serif;">In my previous <a href="http://hamidshahid.blogspot.co.uk/2016/10/set-up-vso-build-agent-on-docker.html" target="_blank">blog post,</a> I wrote about running a VSTS build agent on a docker container. So, it was only logical to try it out on a Windows Container. I had a few pitfalls in my quest to do that and found an <a href="https://github.com/Microsoft/vsts-agent/issues/483" target="_blank">issue</a> on my way, however. it all worked in the end.</span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<i><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Note: </b></span></i></span><br />
<div style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">At the time of writing this blog, running vsts agent is not supported on Windows Nano server. I could only manage to run it on microsoft\windowsservercore image. </span></div>
<h2 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h2>
<h2 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">Window Server 2016 and Windows Containers</span></h2>
<div style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">Windows Server 2016 comes with a full container support powered by built-in operating system features. There is a great session on the internals of windows container on <a href="https://t.co/KFheWpZ144" target="_blank">channel9</a>. There are two mechanisms of setting up containers on Windows - Hyper-V Containers which are effectively light weight virtual machines and Windows Containers. I am going to use Windows Containers</span></div>
<div style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<h3 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">1. Windows 2016 Virtual Machine</span></h3>
<div>
<span style="font-family: "verdana" , sans-serif;">To host my containers, I got a Windows 2016 virtual machine going. At the time of writing this blog, Windows Server 2016 is still at Technical Preview 2 stage and new updates are coming frequently. They can be downloaded from the <a href="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" target="_blank">Microsoft website</a>. </span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;">The minimum build you will require is <u>Windows
2016 Server build 14393</u>.</span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;">Once you have installed windows and are on the virtual machine, open command prompt and type in winver. You will see a dialog like this. Make sure the build number is at least the required version</span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-8F_GUpqcKKI/WBHoR2pEZrI/AAAAAAABEOk/iXwQbRFFRzQmlH63DjbmtIwvqedDSO4jQCLcB/s1600/windowsversion.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "verdana" , sans-serif;"><img border="0" height="334" src="https://4.bp.blogspot.com/-8F_GUpqcKKI/WBHoR2pEZrI/AAAAAAABEOk/iXwQbRFFRzQmlH63DjbmtIwvqedDSO4jQCLcB/s400/windowsversion.png" width="400" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<h3 style="clear: both; text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h3>
<h3 style="clear: both; text-align: left;">
<span style="font-family: "verdana" , sans-serif;">2. Install Containers Feature</span></h3>
<div>
<span style="font-family: "verdana" , sans-serif;">Type in the following command in a PowerShell console window</span></div>
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;">Install-WindowsFeatures Containers</span></blockquote>
<div>
<span style="font-family: "verdana" , sans-serif;">The feature needs a restart so type in the following</span></div>
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;">Restart-Computer -Force</span></blockquote>
<span style="font-family: "verdana" , sans-serif;">Once the machine is restarted, continue with following </span><br />
<h3 style="clear: both;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></h3>
<h3 style="clear: both;">
<span style="font-family: "verdana" , sans-serif;">3. Install Docker</span></h3>
<div>
<span style="font-family: "verdana" , sans-serif;">The docker version deployed from the msi isn't supported on Windows Server 2016 yet. For me downloading the docker msi from the <a href="https://docs.docker.com/docker-for-windows/" target="_blank">docker website</a> didn't work and I got the following error</span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-gKlLqCPJ5-U/WBHqZS2mmaI/AAAAAAABEOw/LZTQMvGiMM8WlMJEwCo8B8rT5JM43NRwgCLcB/s1600/dockererror.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "verdana" , sans-serif;"><img border="0" height="312" src="https://4.bp.blogspot.com/-gKlLqCPJ5-U/WBHqZS2mmaI/AAAAAAABEOw/LZTQMvGiMM8WlMJEwCo8B8rT5JM43NRwgCLcB/s400/dockererror.png" width="400" /></span></a></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;">However, got it working by downloading the following zip file </span></div>
<blockquote class="tr_bq">
<span style="background: white;"><span style="font-family: "verdana" , sans-serif;"><a href="https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip">https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip</a> </span></span></blockquote>
<div>
<span style="font-family: "verdana" , sans-serif;">and extracting it to Program Files. I did it by running the following in my PowerShell console </span></div>
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;"><span style="background: white;">Invoke-WebRequest "</span><a href="https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip"><span style="background: white;">https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip</span></a><span style="background: white;">" -OutFile "$env:TEMP\docker.zip"
-UseBasicParsing</span></span></blockquote>
<span style="font-family: "verdana" , sans-serif;">You will have two executable files in the extracted directory as shown</span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Jk0uoL2P4q4/WBHsLn-z95I/AAAAAAABEO8/gSO_MmnPOm08m8kxna2RXhMThggCoS_DQCLcB/s1600/dockerdirectory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "verdana" , sans-serif;"><img border="0" height="89" src="https://4.bp.blogspot.com/-Jk0uoL2P4q4/WBHsLn-z95I/AAAAAAABEO8/gSO_MmnPOm08m8kxna2RXhMThggCoS_DQCLcB/s400/dockerdirectory.png" width="400" /></span></a></div>
<div style="text-align: center;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;">Add the directory to your path variable. </span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;">Now register the dockerd service by typing the following</span></div>
<blockquote class="tr_bq">
<span style="background: white;"><span style="font-family: "verdana" , sans-serif;">dockerd.exe --register-service</span></span></blockquote>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<h4 style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;">Alternate Installation Option</span></h4>
<div>
<span style="font-family: "verdana" , sans-serif;">After installing docker, I found that the following was a better and easier way of install docker on Windows Server 2016. Type in the following in your powershell console.</span></div>
<div>
<span style="background-color: white; color: #333333; font-family: "verdana" , sans-serif; font-size: 9.75pt;"><br /></span></div>
<blockquote class="tr_bq" style="text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="background-color: white; color: #333333; font-size: 9.75pt;">Install-Module -Name
DockerMsftProvider -Repository PSGallery -Force</span></span> </span></blockquote>
<blockquote class="tr_bq" style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="background-color: white; color: #333333; font-family: "verdana" , sans-serif; font-size: 9.75pt;">Install-Package -Name docker
-ProviderName DockerMsftProvider</span></span></blockquote>
<blockquote class="tr_bq" style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="background-color: white; color: #333333; font-family: "verdana" , sans-serif; font-size: 9.75pt;">Restart-Computer -Force</span></span></blockquote>
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Once installed, verify that docker is running fine your machine by type in the following </span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;">docker run microsoft/sample-dotnet</span></blockquote>
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">You should see a message of the like "Welcome to .Net Core!" on your console window. This means that your docker instance is working fine.</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<span style="font-family: "verdana" , sans-serif; font-size: 18.72px; font-weight: bold;">4. Pulling microsoft/windowservercore image</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">So, this is where I got stuck a bit. I was trying to use <a href="https://hub.docker.com/r/microsoft/nanoserver/" target="_blank">microsoft/nanoserver</a>, which is a fraction of a size of full windows image and support .Net Core. In the end, I found out that running vso agent on server on nano server is <a href="https://github.com/Microsoft/vsts-agent/issues/483" target="_blank">not supported yet</a>.</span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">So, I pulled the full server core image. You can do it by running</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;">docker pull microsoft/windowsservercore</span></blockquote>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">The image is about 8GB and takes some time to download. Once pull, run the image by typing in </span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif;">docker run microsoft/windowsservercore</span></blockquote>
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">At this stage, we are on a windows docker container running Windows 10. I checked in by typing </span><span class="pun" style="background-color: #eff0f1; border: 0px; color: #303336; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">[</span><span class="typ" style="background-color: #eff0f1; border: 0px; color: #2b91af; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">System</span><span class="pun" style="background-color: #eff0f1; border: 0px; color: #303336; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">.</span><span class="typ" style="background-color: #eff0f1; border: 0px; color: #2b91af; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">Environment</span><span class="pun" style="background-color: #eff0f1; border: 0px; color: #303336; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">]::</span><span class="typ" style="background-color: #eff0f1; border: 0px; color: #2b91af; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">OSVersion</span><span class="pun" style="background-color: #eff0f1; border: 0px; color: #303336; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">.</span><span class="typ" style="background-color: #eff0f1; border: 0px; color: #2b91af; font-size: 13px; margin: 0px; padding: 0px; white-space: inherit;">Version</span><span style="font-family: "arial" , "helvetica" , sans-serif;"> a</span><span style="font-family: "arial" , "helvetica" , sans-serif;">nd got the following version</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Major Minor Build Revision</span></span><br />
<span style="font-family: "verdana" , sans-serif;">----- ----- ----- --------</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">10 0 14393 0</span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-size: 18.72px; font-weight: bold;">5. Running VSTS Agent</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-size: 18.72px; font-weight: bold;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now that we have a</span><span style="font-family: "arial" , "helvetica" , sans-serif;"> running container, the steps to run VSO Agent is as simple as running it on any Windows 10 machine. </span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The only complication is the lack of GUI, so I used powershell to download zip file and extract it as follows</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif; font-size: x-small;">Invoke-WebRequest https://github.com/Microsoft/vsts-agent/releases/download/v2.108.0/vsts-agent-win7-x64-2.108.0.zip -outfile vsts-agent-win7-x64-2.108.0.zip</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "verdana" , sans-serif; font-size: x-small;">Expand-Archive -Path .\vsts-agent-win7-x64-2.108.0.zip -DestinationPath C:\vsts-agent </span></blockquote>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">You will see the usual vsts agent's files in the destination directory. Simply type in .\config.cmd and follow instructions.</span><br />
<span style="font-family: "verdana" , sans-serif;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
</span><br />
<div>
<span style="font-family: "verdana" , sans-serif; font-size: 18.72px; font-weight: bold;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "verdana" , sans-serif;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "verdana" , sans-serif;"><br /></span></span></div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com4tag:blogger.com,1999:blog-9108944828477346591.post-61792087289407178012016-10-02T12:34:00.001+01:002018-06-19T17:33:30.018+01:00Set up a VSTS Build Agent on a Docker Container<div dir="ltr" style="text-align: left;" trbidi="on">
In my last blog post, I wrote about setting up .Net core on an Ubuntu 16.04 machine. In this post, I will go a step forward and explain how to set up a container to run as your build agent.<br />
<br />
Containers are brilliant in that they provide a rather lightweight mechanism of setting up desired software in your build agent without installing it on host machine or in a virtual machine. In this post, I will explain setting up a Docker container on a Windows 10 machine, install all the desired software for build agent and running a build to compile an ASP.net core application on it.<br />
<br />
This post is split into following areas<br />
<ul style="text-align: left;">
<li>Setting up Docker on a Windows 10 machine </li>
<li>Create a Docker image to run Build Agent. </li>
<li>Configure and run a Build Agent on Docker. </li>
<li>Run a Build on the newly setup Build Agent. </li>
</ul>
<h2 style="text-align: left;">
<br /></h2>
<h2 style="text-align: left;">
Set up a Docker container on a Windows 10 machine</h2>
<div>
Docker on windows uses Hyper-V to create a linux virtual machine, The Docker daemon is run on this virtual machine.This means that you need at least Windows 10 professional to run Docker. We are using Windows 10 Enterprise. </div>
<div>
</div>
<div>
To install Docker visit the site <a href="https://docs.docker.com/docker-for-windows/">https://docs.docker.com/docker-for-windows/</a> and install the .MSI by clicking the "Get Docker for Windows (stable)" button. The installation will setup Docker for you, by setting up a VM called MobyLinxuVM. It would also add Docker's bin directory to your path, so that the Docker command is available in your command window. </div>
<div>
</div>
<div>
Every docker deployment has a "hello-world" image. To test docker, type in </div>
<blockquote class="tr_bq">
docker run -it hello-world</blockquote>
<div>
You should see the text "Hello from Docker!" in your response.<br />
<h2 style="text-align: left;">
<br /></h2>
<h2 style="text-align: left;">
Create a Docker image to run Build Agent</h2>
</div>
<div>
Now that docker we have a Docker instance running, lets set up a Docker image with all the software needed to run a build agent. The VSO build agent is Node.js based so installing Node.js and it's pre-requisites is a requirement. Also, since the build agent is going to build .Net Core core, we would also be installing .Net core.</div>
<div>
</div>
<div>
To start with, we get a Docker image with the latest version of Ubuntu. At the time of writing this post, the latest version available was 16.04. So we start with getting this version. To do this run</div>
<blockquote class="tr_bq">
docker run Ubuntu:16.04.</blockquote>
It would look for the instance locally and if it's not found download it from Docker hub. Once the command is complete, the Docker Ubuntu 16.04 image will appear in your “docker images” command<br />
<blockquote class="tr_bq">
<a href="http://4.bp.blogspot.com/-93EGoXMy6n8/V_A2ZIV5_OI/AAAAAAAA7H8/WOMUCwiDc04lJ-xFoihr5a8TRyGFm5M0gCK4B/s1600/dockerimages.PNG" imageanchor="1"><img border="0" height="53" src="https://4.bp.blogspot.com/-93EGoXMy6n8/V_A2ZIV5_OI/AAAAAAAA7H8/WOMUCwiDc04lJ-xFoihr5a8TRyGFm5M0gCK4B/s640/dockerimages.PNG" width="640" /></a></blockquote>
<br />
We now install Node.js, npm and vso agent onto the image and store it as another image. To do that the first step is to run the Ubuntu:16.04 image as a container. To do that, run the following command.<br />
<blockquote class="tr_bq">
docker run -t -i Ubuntu:16.04</blockquote>
You will now be on command prompt of root. To run the desire software, run the following commands one after another<br />
<blockquote class="tr_bq">
apt-get update<br />
apt-get install npm nodejs<br />
apt-get install nodejs-legacy<br />
npm install vsoagent-installer –g</blockquote>
My Ubuntu 16.04 instance didn't have https protocol installed, so installed it by running<br />
<blockquote class="tr_bq">
apt-get install apt-transport--https</blockquote>
We will also need to install git on it<br />
<blockquote class="tr_bq">
apt-get install git</blockquote>
<div>
Now, to install .Net Core run the following</div>
<blockquote class="tr_bq">
sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'</blockquote>
<blockquote class="tr_bq">
apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893<br />
apt-get update</blockquote>
<blockquote class="tr_bq">
apt-get install dotnet-dev-1.0.0-preview2-003131 </blockquote>
At this stage, the container contains all the software that you need to run a vso build agent.<br />
<br />
<h2 style="text-align: left;">
<b style="font-size: 18px;">Configure and run Build Agent</b></h2>
<br />
The only thing left to do now is to configure the build agent. The agent installer allows us to easily create multiple agents on a single server. Each agent will run inside its own folder. We create a folder called MyBuildAgent1 and uses it to run the agent. To do this run the following commands<br />
<blockquote class="tr_bq">
mkdir MyBuildAgent1<br />
cd MyBuildAgent1<br />
vsoagent-installer </blockquote>
All commands so far has been run using the root user. We don't want to run the build agent to run as root, so will create a user called buildagentuser and switch to it to use it. Below are the command that you need to run.<br />
<br />
adduser buildagentuser<br />
chown -R buildagentuser /MyBuildAgent1<br />
<br />
The build agent is almost ready to run. At this stage we want to commit this container to save the image. This way we can use it again.<br />
<br />
Step out of Docker container by pressing CTRL+P followed by CTRL+Q. You will be back to command prompt. Now type in "docker ps" to view the list of containers running. You will see a response like following<br />
<br />
<br />
<a href="http://1.bp.blogspot.com/-dtJwHdZ7x8U/V_DWhuro6rI/AAAAAAAA7T0/wmb7eR1rBVAGzslDBariDAFVQ02VdXWPQCK4B/s1600/dockerprocesses.PNG" imageanchor="1"><img border="0" height="88" src="https://1.bp.blogspot.com/-dtJwHdZ7x8U/V_DWhuro6rI/AAAAAAAA7T0/wmb7eR1rBVAGzslDBariDAFVQ02VdXWPQCK4B/s640/dockerprocesses.PNG" width="640" /></a><br />
<br />
Here, the ubuntu:16.04 running container has the Id "aea7e12541d5". We will commit this container to create a new image. To do this, type in the following on your command prompt<br />
<blockquote class="tr_bq">
docker commit -a "Hamid Shahid" -m "Basic vso .net core build agent" aea7e12541d5 basicvsogent:v1</blockquote>
If you are following instructions, please use your container id. You can verify it by running "docker images". You will see the new image in the list of images<br />
<br />
<a href="http://2.bp.blogspot.com/-Bf8FxvGL_wQ/V_DhICdHRmI/AAAAAAAA7VA/_XARfNWXf7o1HuNs3NrIynSca5hoQf7SwCK4B/s1600/dockerimageswithnewimage.PNG" imageanchor="1"><img border="0" height="92" src="https://2.bp.blogspot.com/-Bf8FxvGL_wQ/V_DhICdHRmI/AAAAAAAA7VA/_XARfNWXf7o1HuNs3NrIynSca5hoQf7SwCK4B/s640/dockerimageswithnewimage.PNG" width="640" /></a><br />
<br />
Since, we just "stepped out" of Docker container we were working now, we will now reattach it and run the build agent. To do this type in<br />
<blockquote class="tr_bq">
docker attach aea7e12541d5</blockquote>
Now start the build agent by running<br />
<blockquote class="tr_bq">
node agent/vsoagent</blockquote>
You will be prompted about your VSTS url and the credentials to connect to. I used the following options in the prompt<br />
<br />
<a href="http://3.bp.blogspot.com/-zBaXru_F8Xo/V_DltdmH9UI/AAAAAAAA7Vw/FAVeRSCNkYsd5wo4U0iuVUvgFL1iZJ7eACK4B/s1600/startbuildagent.PNG" imageanchor="1"><img border="0" height="135" src="https://3.bp.blogspot.com/-zBaXru_F8Xo/V_DltdmH9UI/AAAAAAAA7Vw/FAVeRSCNkYsd5wo4U0iuVUvgFL1iZJ7eACK4B/s640/startbuildagent.PNG" width="640" /></a><br />
<br />
The user you specify must have the service "Service Account" role in the agent pool you specified. In my case, I had added them to the VSTS group "Agent Pool 1 Service Account Users".<br />
<br />
<a href="http://2.bp.blogspot.com/-8iOAuk05VpA/V_DnSV-qoVI/AAAAAAAA7WE/-F_L98XQ9EUfF30b00kSJwl-alG4AMRnQCK4B/s1600/permissions.PNG" imageanchor="1"><img border="0" height="129" src="https://2.bp.blogspot.com/-8iOAuk05VpA/V_DnSV-qoVI/AAAAAAAA7WE/-F_L98XQ9EUfF30b00kSJwl-alG4AMRnQCK4B/s640/permissions.PNG" width="640" /></a><br />
.<br />
Now that my build agent is now running, I will create now create a simple build definition to run the build. We had already created a simple .Net core Asp.net application.<br />
<br />
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: normal; letter-spacing: normal; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">
</div>
<br />
<h2 style="color: black; font-family: "times new roman"; font-style: normal; letter-spacing: normal; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">
Run a build on the new build agent</h2>
<div>
We will now create a very simple build definition and execute it on the new agent. Our build definition has three simple steps.</div>
<div>
</div>
<div>
<a href="http://3.bp.blogspot.com/-vnTbBjTclsE/V_DtNPC80II/AAAAAAAA7XU/l2oUAoSHZl0bz08pvAfF7RVcdVXOrVNmgCK4B/s1600/build.PNG" imageanchor="1"><img border="0" height="244" src="https://3.bp.blogspot.com/-vnTbBjTclsE/V_DtNPC80II/AAAAAAAA7XU/l2oUAoSHZl0bz08pvAfF7RVcdVXOrVNmgCK4B/s640/build.PNG" width="640" /></a></div>
<div>
</div>
<div>
1) In the first step, invoke dotnet with the argument "restore".</div>
<div>
2) In the second, invoke dotnet with the argument "build". Make sure, you set the working folder to the source directory</div>
<div>
3) In the third step, set the contents to "**/*.dll".</div>
<div>
</div>
<div>
Since, we used the Default Build Pool, set it to use the default build agent. Now run the build and let's monitor the build agent command window. We should see messages regarding the statue of the build job.</div>
<div>
</div>
<div>
<a href="http://1.bp.blogspot.com/-LjG2QaXJDUc/V_Dv0vjAdoI/AAAAAAAA7YU/sTicnoSoorM3PdlmzeT2klF6RErZYHk-wCK4B/s1600/buildjob.PNG" imageanchor="1"><img border="0" height="73" src="https://1.bp.blogspot.com/-LjG2QaXJDUc/V_Dv0vjAdoI/AAAAAAAA7YU/sTicnoSoorM3PdlmzeT2klF6RErZYHk-wCK4B/s640/buildjob.PNG" width="640" /></a></div>
<div>
</div>
<div>
<br />
This is so awesome. Now that we have the Docker image captured as well, we can start other build agents and distribute our build load across containers. This is far more efficient than running virtual machines as build agents.</div>
<div>
</div>
<div>
The above would only work for dot net core applications. In my next post, I will write about how to set up VSTS agents on Windows Containers, where we would also have the ability to build .Net applications.</div>
</div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com2tag:blogger.com,1999:blog-9108944828477346591.post-18804966582932127702016-05-27T01:28:00.004+01:002016-05-27T10:54:04.108+01:00Installing .Net Core on Ubuntu 16.04 LTS<div dir="ltr" style="text-align: left;" trbidi="on">
The recent development by Microsoft in the ASP.Net core space is fascinating. With the <a href="http://www.hanselman.com/blog/ASPNET5IsDeadIntroducingASPNETCore10AndNETCore10.aspx" target="_blank">introduction of ASP.Net Core 1.0</a> at the start of the year and then the recently released <a href="https://blogs.msdn.microsoft.com/webdev/2016/05/16/announcing-asp-net-core-rc2/" target="_blank">ASP.Net Core 1.0 RC2</a>, I thought it would be a good idea to try it out on a Linux box.<br />
<br />
The installation proved to be a bit trickier than I thought, so sharing my experience so as to help other out. The flavour of Linux I used was <u>Ubuntu 16.04 LTS.</u><br />
<br />
At the time of writing this post, the instructions present on Microsoft .Net Core <a href="https://www.microsoft.com/net/core#ubuntu" target="_blank">website</a> are for Ubuntu 14.04. Tried to follow the steps described on the website. However, execute dotnet failed with the following error on my machine<br />
<blockquote class="tr_bq">
Failed to initialize CoreCLR, HRESULT: 0x80131500</blockquote>
<br />
So tried to proceed with some other steps. In general, installing .Net on Ubuntu require the following steps<br />
<br />
1) Add .net repo to trusty sources list<br />
2) Add key for the newly added trusted source<br />
3) Install dotnet<br />
<br />
<br />
To do the above, open up a terminal on your Ubuntu machine.<br />
<br />
To add repo, run the following command<br />
<blockquote class="tr_bq">
sudo sh -c 'echo "deb [arch=amd64] http://apt-mo.trafficmanager.net/repos/dotnet/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'</blockquote>
To add the key, run the following command<br />
<blockquote class="tr_bq">
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893</blockquote>
Once, the above done, it's bes to update everything by running<br />
<blockquote class="tr_bq">
sudo apt-get update</blockquote>
Now that everything is done, .Net can be installed by running<br />
<blockquote class="tr_bq">
sudo apt-get install dotnet</blockquote>
<br />
The above command, however, didn't work for me. It failed with the following error<br />
<span style="background-color: white; color: #333333; font-family: "helvetica neue" , "helvetica" , "segoe ui" , "arial" , "freesans" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 14px; line-height: 22.4px;"><br /></span>
<br />
<blockquote class="tr_bq">
<span style="background-color: white; color: #333333; font-family: "helvetica neue" , "helvetica" , "segoe ui" , "arial" , "freesans" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 14px; line-height: 22.4px;">The following packages have unmet dependencies: </span><span style="background-color: white; color: #333333; font-family: "helvetica neue" , "helvetica" , "segoe ui" , "arial" , "freesans" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 14px; line-height: 22.4px;">dotnet : Depends: libicu52 (>= 52~m1-1~) but it is not installable </span><span style="background-color: white; color: #333333; font-family: "helvetica neue" , "helvetica" , "segoe ui" , "arial" , "freesans" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 14px; line-height: 22.4px;">E: Unable to correct problems, you have held broken packages.</span></blockquote>
<br />
The error is quite self-descriptive, and the answer is to install libicu package. Ran the commnad<br />
<blockquote class="tr_bq">
sudo apt-get install libicu-dev</blockquote>
<br />
The above command installed the libicu55 on my machine, whilst .Net core has a dependency on libicu52. Fortunately, the package is available for download <a href="http://packages.ubuntu.com/trusty/amd64/libicu52/download" target="_blank">here</a>.<br />
<br />
Once downloaded, the package can be installed by running<br />
<blockquote class="tr_bq">
sudo dpkg -i libicu52_52.1-3ubuntu0.4_amd64.deb </blockquote>
<br />
Now that the pre-requisite is installed, installing dotnet is simply a matter of running the following<br />
<blockquote class="tr_bq">
sudo apt-get install dotnet</blockquote>
<br />
The above worked for me this time around. To test that it installed correctly, just type dotnet new in a new folder. It will create files for your .net project.<br />
<br />
<b>Please Note:</b> Make sure that you set permissions to execute downloaded files described in this post. </div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com62tag:blogger.com,1999:blog-9108944828477346591.post-61271465216827951552015-10-03T00:26:00.000+01:002015-10-03T00:27:03.195+01:00Shelveset Comparer now supports Visual Studio 2015<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: Arial, Helvetica, sans-serif;">Last year, I created a visual studio extension that allow users to compare two
shelvesets. </span><span style="font-family: Arial, Helvetica, sans-serif;">The extension has proved to be quite popular. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">The visual studio 2015 version of the extension is out today and can be download from <a href="https://visualstudiogallery.msdn.microsoft.com/a454ab06-cae0-42a2-89a2-e1060ed4d064" target="_blank">here</a>. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">Please free free to use it and give your feedback.</span></div>
Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com47tag:blogger.com,1999:blog-9108944828477346591.post-6244941793757855802015-03-02T08:48:00.001+00:002015-03-02T08:48:49.708+00:00“No tracking information on current branch” while doing git pull<p>Git continues to amuse me. There sheer options and versatility that you get in creating branches, merges and tracking commits, make it a such a powerful source code repository. It also means a steep learning curve for a lifelong TFSVC and VSS . </p> <p>This is the first of a series of posts, I will write about some common start up mistakes I have done or seen others doing. I hope it would be helpful for the wider community.</p> <p>To demonstrate my git repository clearly, I am using <a href="https://github.com/dahlbyk/posh-git">posh-git</a>, which gives a view of my current git branch and also shows local, active and remote branches in different colours</p> <h3>Created branch but failed to created tracking information.</h3> <h3></h3> <p>This is one of the most common mistakes. To describe it, I start with listing all my local and remote branch. To do that I type in “git branch –a” as shown</p> <p><a href="http://lh4.ggpht.com/-lMVNbFx2ZIA/VPQjyQ7-O9I/AAAAAAAAE6I/vObT0Dh8YYc/s1600-h/GitBranchAll%25255B3%25255D.png"><img title="GitBranchAll" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitBranchAll" src="http://lh3.ggpht.com/-q6rkO0Wm-uk/VPQjzO8afuI/AAAAAAAAE6Q/7-_DkmVTZhM/GitBranchAll_thumb%25255B1%25255D.png?imgmax=800" width="666" height="348"></a> </p> <p></p> <p>As you can see with the asterisk against master that my current local repository is “master”. </p> <p>Now lets say that I want to work on the remote “TeamBuild/Staging” branch . To do that I created a new local branch naming it same as the server</p> <p><a href="http://lh5.ggpht.com/-A5MhhOzWtBA/VPQjzjneQ9I/AAAAAAAAE6Y/gp9xq7J3ZVI/s1600-h/GitBranchCheckoutNew%25255B3%25255D.png"><img title="GitBranchCheckoutNew" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitBranchCheckoutNew" src="http://lh6.ggpht.com/-TtoCa9k0v9s/VPQj0aZc5LI/AAAAAAAAE6g/T82q7eDMxFk/GitBranchCheckoutNew_thumb%25255B1%25255D.png?imgmax=800" width="613" height="95"></a> </p> <p>The new branch is created successfully and I am switched on to the new branch as shown by running another “git branch –a”</p> <p><a href="http://lh5.ggpht.com/-cMwtKi4ZFm8/VPQj1EIQ3yI/AAAAAAAAE6o/GQj7eIxEbCI/s1600-h/GitBranchCheckoutPostCreation%25255B6%25255D.png"><img title="GitBranchCheckoutPostCreation" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitBranchCheckoutPostCreation" src="http://lh4.ggpht.com/-aft8M6KzEWw/VPQj16EAxLI/AAAAAAAAE6w/YkwCE9uj7qM/GitBranchCheckoutPostCreation_thumb%25255B2%25255D.png?imgmax=800" width="639" height="376"></a> </p> <p>You can see that the current branch is now switched to “teambuild/staging”. All good so far.</p> <p>However, when I do a “git pull”, I got the following message.</p> <p><a href="http://lh4.ggpht.com/-FLjBoN5zIX0/VPQj2TBu05I/AAAAAAAAE64/mK8W9eex6tk/s1600-h/GitPullNoTrackingBranch%25255B3%25255D.png"><img title="GitPullNoTrackingBranch" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitPullNoTrackingBranch" src="http://lh5.ggpht.com/-WRZlyCkro2E/VPQj3XInKdI/AAAAAAAAE7A/jJe1botwIGs/GitPullNoTrackingBranch_thumb%25255B1%25255D.png?imgmax=800" width="615" height="199"></a> </p> <h3></h3> <p>So, what has gone wrong. A quick peek into the Git config file, tells me that the new branch is created without tracking it to the remote branch with the same name.</p> <p><a href="http://lh6.ggpht.com/-8i6zbI9bCEI/VPQj4En1kuI/AAAAAAAAE7I/3sO1T2DJ2Vo/s1600-h/GitConfigBefore%25255B3%25255D.png"><img title="GitConfigBefore" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitConfigBefore" src="http://lh5.ggpht.com/-TlbVpwRnVJw/VPQj4zf55qI/AAAAAAAAE7Q/GPENX7QRFpg/GitConfigBefore_thumb%25255B1%25255D.png?imgmax=800" width="618" height="513"></a></p> <p>As you can see that there is no mention of the newly created branch.</p> <h3>Cause</h3> <p></p> <p></p> <p></p> <p></p> <p>The reason no tracking information was created is because <strong><u>Git is case sensitive</u></strong>. So, the branch“teambuild/staging” is not same as “TeamBuild/Staging”. For windows / TFS users, they look the same but Git would treat them as two separate branches.</p> <h3>Resolution</h3> <p>You can add tracking information in a branch using the “git branch –u” command. So typing the following will add tracking information with the remote branch and you will be be up and running</p> <p><a href="http://lh3.ggpht.com/-PUo2uFJlCsU/VPQj5TcxlhI/AAAAAAAAE7Y/LoPmdA0OeLE/s1600-h/GitTrack%25255B3%25255D.png"><img title="GitTrack" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitTrack" src="http://lh6.ggpht.com/-7hL-ewyHo44/VPQj6FqVbyI/AAAAAAAAE7g/rPAJAAf7ILE/GitTrack_thumb%25255B1%25255D.png?imgmax=800" width="760" height="59"></a> </p> <p>You can see that the tracking information is added by peeking into the git config file. Notice the couple of lines in the end to see details of the /staging/teambuild branch added to git config.</p> <p><a href="http://lh5.ggpht.com/-OdDJVIAwBUg/VPQj7P0ox6I/AAAAAAAAE7o/Nkv79IvZE6o/s1600-h/GitConfigAfter%25255B3%25255D.png"><img title="GitConfigAfter" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="GitConfigAfter" src="http://lh5.ggpht.com/-DYru6spsCoY/VPQj7-AJrHI/AAAAAAAAE7w/00qLIApHkt0/GitConfigAfter_thumb%25255B1%25255D.png?imgmax=800" width="675" height="572"></a></p> <h3>Don’t try it at home</h3> <p>You will git into a bit of a pickle if are using Windows and you create two local branches that differentiate just by casing. Since git creates a folder within the refs/head folder in the git directory. If the names vary only by casing, it would have two branches tracked on one folder which would confuse it. So be mindful with casing while working with Git branches.</p> <div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:95ad5a2c-1423-4ccc-934b-eb96b4cfd5a8" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">Technorati Tags: <a href="http://technorati.com/tags/TFS" rel="tag">TFS</a>,<a href="http://technorati.com/tags/Git" rel="tag">Git</a>,<a href="http://technorati.com/tags/Branches" rel="tag">Branches</a>,<a href="http://technorati.com/tags/Common+Mistakes" rel="tag">Common Mistakes</a>,<a href="http://technorati.com/tags/Casing" rel="tag">Casing</a>,<a href="http://technorati.com/tags/There+is+no+tracking+information+for+current+branch" rel="tag">There is no tracking information for current branch</a></div> Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-3801296177489504492014-11-17T00:21:00.001+00:002014-11-17T00:21:19.737+00:00Shelveset Comparer Updated<p>Shelveset Extension is a visual studio extension that I first published at the start of this year. The extension provides a functionality that is otherwise missing in both Visual Studio and Team Foundation Server that is to compare the contents of two shelvesets. I felt that need for it as our team used shelvesets to pass work around and tracking what has changed since the time a shelveset was taken was not always obvious. </p> <p>There extension has proved popular I have been trying to keep up with comments and feedback on it. This update was due for some time. The view of the extension in team explorer has changed a bit to show options for typing in two users. This allows for comparing shelvesets between two users. However, unlike the first release of the extension, there is still one list to display all users. To separate out shelvesets of two users, an “Owner” column has been added. The column headers are made clickable as well and will sort the rows based on the clicked column.</p> <p><a href="http://lh4.ggpht.com/-xwsYcLxMxxo/VGk_cceNSdI/AAAAAAAAE48/RMfUtk4uX7U/s1600-h/Screenshot2%25255B3%25255D.png"><img title="Screenshot2" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="Screenshot2" src="http://lh5.ggpht.com/-iANw1L-xeyc/VGk_dldho1I/AAAAAAAAE5E/-QUMGyjkhPU/Screenshot2_thumb%25255B1%25255D.png?imgmax=800" width="717" height="477"></a> </p> <p>Another feature added is the Options panel, allowing users to select whether they want to view the extension as a Team Explorer button or not. Another option is to hide the second user.</p> <p> </p> <p><a href="http://lh6.ggpht.com/-cbWKtRAUhFU/VGk_efo3m9I/AAAAAAAAE5M/Ket9sK42KbQ/s1600-h/Screenshot4%25255B3%25255D.png"><img title="Screenshot4" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="Screenshot4" src="http://lh3.ggpht.com/-yjlmI0xSXi8/VGk_faBgseI/AAAAAAAAE5U/_3aGiyyd72w/Screenshot4_thumb%25255B1%25255D.png?imgmax=800" width="445" height="161"></a> </p> <p>The options are there to allow users to customise the view as per their needs.</p> <p> </p> <p>Apart from the new functionality, several fixes and performance improvements have been made</p> <p> </p> <p>Going forward, there is going to be another release by the end of this year, where I will be adding feature to search on a shelveset name. There will be further optimisation in the performance when comparing the contents of two shelvesets.</p> <div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:93dd22c2-5ec4-4886-b6aa-31e0753e2e9a" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">Technorati Tags: <a href="http://technorati.com/tags/TFS" rel="tag">TFS</a>,<a href="http://technorati.com/tags/Team+Foundation+Server" rel="tag">Team Foundation Server</a>,<a href="http://technorati.com/tags/Shelveset" rel="tag">Shelveset</a>,<a href="http://technorati.com/tags/Visual+Studio" rel="tag">Visual Studio</a></div> Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0tag:blogger.com,1999:blog-9108944828477346591.post-45408956262838069352014-05-15T23:52:00.000+01:002014-05-16T00:23:29.392+01:00Feature Toggler – a Simple feature toggle library for .Net<p>So, you have decided to use Feature Toggling as your branching strategy. You don’t want the hassle of merging and branching and are confident that developers and testers can handle the additional complexity that comes with Feature Toggles. The next step is to decided how to go about using toggles. The simplest and most popular method of doing is to have feature toggles set in configuration files</p> <p>Ideally, you would want a library that would take care of feature toggling. All you would need to do is to define the features and their toggle value in the configuration file and be able to check if a feature is available with a simple check. Some thing which for a configuration like below </p> <blockquote> <p><font color="#0000ff"><<font color="#795540">featureConfiguration</font>><br> <<font color="#795540">features</font>><br> <<font color="#795540">add</font> <font color="#ff0000">name</font>="PrivateProfiles" <font color="#ff0000">toggle</font>="on" /><br> <<font color="#795540">add </font><font color="#ff0000">name</font>="Photosharing" <font color="#ff0000">toggle</font>="off" /><br> <<font color="#795540">add </font><font color="#ff0000">name</font>="Videos" <font color="#ff0000">toggle</font>="1" /><br> <<font color="#795540">add </font><font color="#ff0000">name</font>="bookmarks" <font color="#ff0000">toggle</font>="true" /><br> </<font color="#795540">features</font>><br></<font color="#795540">featureConfiguration</font>></font></p></blockquote> <p>would allow having code like following </p> <blockquote> <p>if (FeatureManager.HasFeature("PrivateProfiles")){</p> <p>…</p> <p>}</p></blockquote> <p>Having looked around, there were three libraries of note already available, which were</p> <ol> <li><a href="https://github.com/benaston/NFeature">NFeature</a> <li><a href="https://github.com/benaston/NFeature">FeatureToggle</a>, and <li><a href="https://github.com/mexx/FeatureSwitcher">FeatureSwticher</a></li></ol> <p>This <a href="http://david.gardiner.net.au/2012/07/feature-toggle-libraries-for-net.html">blog post</a> gives a good comparison of them and their usability. Having used all three, I felt that all of them, though thorough, were overly complicated for the very simple scenario that I wanted to use. For example, NFeature requires you to create enumerations for all features added in configuration file. </p> <p>I decided to create a new very simple feature toggling library. <a title="https://github.com/hamidshahid/FeatureToggler" href="https://github.com/hamidshahid/FeatureToggler">https://github.com/hamidshahid/FeatureToggler</a></p> <p>The library is available as NuGet. Simply type “Install-package FeatureToggler” in the package manager window of your application. It will add references, add a configuration section in your configuration files and adds a few sample features in your configuration file. </p> <p>Once you have the reference added, simply add features in the features collection and use them in your code using the FeatureManager.HasFeature(“”) method. Happy Coding!!</p> <div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:10860cfc-7fdb-4783-90df-66a0ee452876" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">Technorati Tags: <a href="http://technorati.com/tags/C%23" rel="tag">C#</a>,<a href="http://technorati.com/tags/feature+Toggling" rel="tag">feature Toggling</a>,<a href="http://technorati.com/tags/NuGet" rel="tag">NuGet</a></div> Hamidhttp://www.blogger.com/profile/10465713442151551945noreply@blogger.com0